Contentful Integration Guide

Connecting a frontend to Contentful means choosing between its Delivery API (published content) and Preview API (drafts), picking REST or GraphQL, and wiring deterministic cache invalidation around sys.updatedAt and webhooks. This guide covers those patterns framework-agnostically, with the schema-sync and secure-preview details that scale across Jamstack and hybrid rendering. It sits within Platform Integration Deep Dives.

Contentful APIs and what each is forThe Content Delivery API, Content Preview API, GraphQL Content API, Content Management API and Sync API compared on content state, typical use and where tokens may live.APIContentTypical useToken locationDelivery (REST)publishedpages, CDN-cachedserver, edgePreview (REST)drafts + publisheddraft modeserver onlyGraphQL Contentpublished or previewcomponent queriesserver only for previewManagementread and writemigrations, toolingCI and back officeSyncdeltas since tokensearch indexing, exportsjobs only
Only delivery tokens may ever be used where readers could see them; everything else stays on servers.

Integration Contract

A Contentful integration rests on a short contract. Space and environment: one space per product or brand, production read through the master alias, and migration environments created from it for model changes. Tokens: a delivery token per environment for published reads, a preview token on the server for drafts, and management access only in CI. Model as code: content types changed through versioned migration scripts, never by hand in production. Types: TypeScript types generated from the model in CI. Events: signed webhooks for publish, unpublish and delete, routed to one handler that revalidates by tag. Fetching: one data layer that knows about Contentful; components receive domain objects, not raw entries.

API Architecture & Environment Configuration

Contentful exposes two read endpoints — the Delivery API for published states, the Preview API for drafts — and both speak REST and GraphQL. GraphQL eliminates over-fetching and resolves nested data in one request; REST gives simpler cache-key semantics and plays better with edge CDNs that penalize high query-string entropy. Standardize on GraphQL for multi-tenant schema validation; reach for REST when query-string cardinality would fragment your CDN cache.

Every request needs a Space ID and a scoped token. Delivery tokens are read-only and environment-bound; CMA writes require OAuth2 or a Personal Access Token with explicit write scopes. Never ship CMA credentials in a client bundle — route management and preview mutations through a server-side proxy.

Dotenv
CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_ACCESS_TOKEN=your_delivery_token
CONTENTFUL_PREVIEW_TOKEN=your_preview_token
CONTENTFUL_ENVIRONMENT=master

A framework-agnostic client centralizes auth, error handling, and environment toggling. This TypeScript wrapper standardizes GraphQL execution across Vite, Astro, or custom SSR runtimes:

TypeScript
// lib/contentful-client.ts
const BASE_URL = 'https://graphql.contentful.com/content/v1/spaces';

export async function contentfulQuery<T>(query: string, variables = {}, preview = false) {
  const token = preview ? process.env.CONTENTFUL_PREVIEW_TOKEN : process.env.CONTENTFUL_ACCESS_TOKEN;
  const spaceId = process.env.CONTENTFUL_SPACE_ID;
  const env = process.env.CONTENTFUL_ENVIRONMENT || 'master';
  
  const res = await fetch(`${BASE_URL}/${spaceId}/environments/${env}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({ query, variables }),
  });
  
  if (!res.ok) throw new Error(`Contentful API error: ${res.status} ${res.statusText}`);
  const json = await res.json();
  if (json.errors) throw new Error(json.errors.map((e: any) => e.message).join(', '));
  return json as { data: T };
}

Data Fetching & Caching Patterns

Contentful responses carry sys.updatedAt timestamps and etag headers, which give you deterministic invalidation. Layer three caches, aligned with the HTTP Caching reference:

  1. Edge/CDN: Apply Cache-Control: public, max-age=300, stale-while-revalidate=600 to published queries. Key REST by URL or query-hash, GraphQL by persisted query, so cache hits stay consistent.
  2. Application: Front high-frequency requests with an in-memory or Redis cache, keyed by operation name plus variables, invalidated on webhook.
  3. ISR/SSR: For hybrid renderers, drive on-demand revalidation from Contentful webhooks rather than polling sys.updatedAt on every build.

Verify webhook payloads with Contentful’s request signing, as described in verifying Contentful webhook signatures, before purging, and target specific content types or entry IDs — blanket invalidation defeats the cache. When designing persisted queries, follow the GraphQL API documentation so cache keys stay stable across environments.

The three cache layers sit between a request and Contentful, with publishes invalidating only what changed:

Cache layers between the frontend and ContentfulA frontend request passes the edge cache, then an application cache, then an ISR or SSR render that calls the Contentful Delivery API; a signed publish webhook is verified and triggers a targeted purge by content type or entry id in the edge and application caches.FrontendrequestEdge / CDNcacheApp cacheISR / SSRrenderContentfulDelivery APISigned publishwebhookTargeted purgeentry, typemissmissverify
Publishes invalidate exactly what changed, at every layer.

Environments and Aliases

Contentful spaces contain environments, full copies of content and content model, and aliases, such as master, that point to one environment. Production traffic should read through the alias, not a concrete environment id. Model changes are then developed and migrated in a new environment cloned from production, tested with a preview deployment pointed at it, and released by switching the alias, which is atomic for readers. The frontend needs no deploy for the switch, but it must purge or revalidate caches afterwards, because alias switches fire no entry webhooks. The environment aliases guide walks through the sequence.

Rich Text Rendering

Contentful’s rich text is a JSON document, not HTML. It contains paragraphs, headings, lists and marks, plus embedded entries, inline entries and asset links that reference other content by id. Render it with the official renderer, mapping each node type to your components, and resolve embedded entries and assets from the links section of the GraphQL response or the includes of REST responses. Treat unknown embedded entry types like unknown blocks: render nothing and log, so a new content type in rich text never breaks a page. The rich text guide shows the mapping.

Assets and the Images API

Contentful serves assets from its own CDN, and images can be transformed on the fly with URL parameters: width and height, fit and focus area, format with fm=avif or fm=webp, and quality. That makes a custom image loader straightforward: build the URL from the asset’s base URL and the requested width, and let Contentful’s CDN do the work, as described in automating Next.js image optimization. Asset URLs include the asset’s id and a version token in the path, so an updated image gets a new URL and caches never need purging. Always request the asset’s width, height and description fields with the URL; dimensions prevent layout shift and the description is a sensible default for alt text, overridable per usage.

Localization in Contentful

Contentful localizes per field. Each field can be marked localizable, and each locale can have a fallback locale, forming chains such as de-AT → de → en-US. Delivery API requests for a locale return fallback values for empty fields automatically, which is convenient but hides which values fell back. When the frontend needs that information, for notices, hreflang membership or coverage reports, request locale=* in REST and resolve the chain in code, as shown in configuring fallback chains. Slugs should be localizable fields when URLs are translated, and validated for uniqueness per locale with a custom app or a check in the publish webhook.

Security and Tokens

Contentful’s token types map cleanly onto least privilege. Delivery tokens read published content from specific environments and can live on servers and edge functions; they are not secret in the strict sense, since the content they read is public anyway, but keep them out of client bundles to avoid abuse of your rate limits. Preview tokens read drafts and must stay on servers, used only in authenticated preview routes. Management tokens can change content and models and belong only in CI and back-office tools, ideally as app identities rather than personal access tokens tied to an employee. Scope delivery and preview tokens to the environments they need, rotate them on a schedule, and keep an inventory, as described in RBAC and audit trails.

Schema Synchronization & Type Safety

Manually maintained interfaces drift the moment an editor renames a field. Generate types instead — from the CMA or GraphQL introspection — and treat the schema as a versioned contract: commit model changes with frontend code and fail CI when a required field goes missing. Setting up TypeScript Types from Headless CMS Schemas covers the extraction pipeline, including union types for rich-text nodes and compile-time asset-reference validation.

Preview & Editorial Workflow

Preview lets editors see drafts before publish. Point the Preview API at draft states, bypassing the CDN, behind a route that takes a secret, entry ID, and locale. Validate the secret server-side, fetch the draft, and inject it into the render path — for static frameworks, via a server-side preview handler so you don’t trigger a full rebuild.

Contentful differs from Sanity Studio Customization and Strapi Self-Hosted Setup, but the preview rules are the same everywhere: isolate draft traffic, validate the request, and never pollute the production cache.

Framework-Specific Implementation

These patterns map directly onto meta-frameworks. For App Router routing, middleware, and ISR specifics, see Integrating Contentful with Next.js step by step. The one rule that trips teams up: don’t mix client-side fetches across server-rendering boundaries, or you’ll lose the cache directives the framework relies on.

Rate Limits and Large Builds

Contentful’s delivery APIs have per-second rate limits, and the GraphQL API additionally limits query complexity. Static builds that fetch thousands of entries in parallel hit them quickly. Pool requests, honour the X-Contentful-RateLimit-Reset header on 429 responses, request only the fields you render, and paginate with sensible page sizes. For jobs that need all content, such as search indexing, use the Sync API, which returns everything once and then only changes, instead of re-querying the whole space. The rate limits and Sync API guide covers both.

Content Modeling Notes for Contentful

A few Contentful specifics affect modeling. References are links to entries or assets, resolved through include depth in REST, up to ten levels, or nested selections in GraphQL, where each level adds to query complexity. Keep reference depth shallow and resolve deeper data separately. Validations on link fields can restrict which content types may be referenced, which is how page-builder blocks are constrained to a known set, the basis for the discriminated unions described in modeling page-builder blocks. JSON object fields are flexible but unvalidated; prefer structured content types. And each space has limits on the number of content types, fields per type and locales depending on the plan, so check them before designing a model that depends on many types.

Worked Example

A media company moved from a WordPress site to Contentful and Next.js. Its first version fetched every page with REST includes at depth ten, used the delivery token in client components, and rebuilt the whole site on every publish, which took twenty minutes and regularly hit rate limits. The second version introduced a data layer with GraphQL queries per template, generated types, incremental regeneration with tags from signed webhooks, the Images API through a custom loader, and model changes through migrations and alias switches. Publishes appeared in under ten seconds, builds stopped hitting rate limits, and the next model change, a new article layout, shipped without any downtime.

Webhooks and Invalidation

Contentful sends webhooks for entry and asset events: create, save, auto-save, publish, unpublish, archive and delete, filtered per webhook by event type, content type and environment. For cache invalidation, subscribe to publish, unpublish and delete only; save and auto-save events fire constantly while editors type and should trigger nothing on the public site, although they are useful for live preview. Enable request signing on each webhook and verify the signature over the raw body before acting. Route webhooks from all environments to one handler that maps the environment id to the right deployment, as described in routing webhooks by environment, and remember that alias switches, environment clones and bulk imports need their own invalidation steps because they may not fire the entry events you expect. Tag fetches with entry ids and content types, so a publish revalidates exactly the pages that use the changed entry, including listings.

Live Preview and Visual Editing

Beyond draft-mode page previews, Contentful offers live preview inside its web app, with an SDK that subscribes the preview page to field changes and maps rendered elements back to fields for click-to-edit. Integrate it only in draft mode and load it dynamically, so its JavaScript never reaches production visitors. Tag elements with field identifiers from the entry data rather than hard-coding them, and keep the preview page on the same component tree as production, so what editors see is what readers will get. The live editing patterns topic compares approaches across platforms, and the same rules apply: isolate draft traffic, bypass caches, and never let preview tokens or scripts leak into public responses.

Choosing Contentful

Contentful suits teams that want a managed, enterprise-ready content platform with strong governance: roles and permissions, environments, audit logs on higher plans, and a large app ecosystem. Its trade-offs are cost at scale, content models defined in a web interface unless teams adopt migration scripts consistently, per-plan limits on environments and locales, and rate limits that shape build strategies. Compared with Sanity, it offers less customization of the editing interface but more built-in structure; compared with Strapi or Directus, it removes hosting work at the price of less control over infrastructure and data location. Evaluate it with a proof of concept that includes preview, localization and a model migration, since those are where integrations spend most of their effort, and include a realistic content volume so rate limits and build times show up early.

Error Handling & Resilience

Handle Contentful failures at the fetch boundary. Distinguish 404s, which are legitimate missing content, from 401 and 403, which indicate token or environment problems and should alert, and from 429 and 5xx, which should be retried with backoff. GraphQL responses can contain errors alongside partial data; decide per page whether partial data is acceptable. When the API is unavailable, serve stale cached pages rather than errors, and make sure a failed build does not deploy empty pages.

Testing & Observability

Generate types from the content model in CI and fail on mismatches with components. Record fixture responses per content type, including rich text with every embedded type, and render them in component tests. Log request counts, 429s and latency per query, which reveals queries that are too large or too frequent. Monitor webhook delivery in Contentful’s webhook logs and your own handler logs, as described in monitoring webhook delivery.

Build-time API requests for a 6,000-page siteContentful API requests during a full static build with unpooled per-page queries, with pooled queries selecting only rendered fields, and with incremental regeneration after the first build.Unpooled, all fields18400 requests per buildPooled, selected fields6300 requests per buildIncremental after first build40 requests per build
Most of the saving came from not rebuilding everything on every publish.

Frequently Asked Questions

GraphQL or REST for Contentful?

GraphQL for component-driven pages with nested references; REST for simple lists, CDN-friendly caching and the Sync API. Many projects use both.

Should the frontend read from master or an environment id?

From the master alias, so model releases can switch environments without a deploy.

Can preview use the GraphQL API?

Yes, with preview: true and the preview token, on the server only, bypassing caches.

How do we handle locales?

Contentful localizes per field with fallback locales configured in the space. Request a locale explicitly, or locale=* in REST to see which locales have values.

How do we test against Contentful in CI?

Use a dedicated test environment populated from fixtures, or recorded API responses for unit tests. Avoid running CI against production content, which changes and makes tests flaky.

What plan limits matter most for integrations?

API rate limits, the number of environments, locales and content types, and webhook counts. Check them early against your plan, because they constrain the architecture, especially preview and build strategies.

Does Contentful work with frameworks other than Next.js?

Yes. The APIs are framework-agnostic; Astro, Nuxt, Remix and SvelteKit integrations follow the same patterns of server-only tokens, tagged caching and signed webhooks.

Is Contentful’s GraphQL API complete?

It covers delivery and preview reads for all content types. Management operations, sync and some filters remain REST-only, so most real integrations end up using both APIs.