Skip to content
Next.js7 min read

Next.js with a Headless CMS: Patterns That Work

Pairing Next.js with a headless CMS is a proven stack, but the integration details decide whether it stays pleasant. Rendering strategy, block modelling, previews and webhooks.

We have shipped Next.js sites against Contentful, Sanity, Strapi, Storyblok and WordPress in headless mode, and the lesson is consistent: the CMS you pick matters less than the patterns you integrate it with. The same four decisions come up every time — how pages render, how content is modelled, how editors preview, and how published changes reach the live site.

Render static, invalidate on publish

Content sites should be static. Fetch CMS content in Server Components with cache tags, set a long or infinite revalidation window, and let the CMS webhook invalidate tags when editors publish. Visitors get cached pages from the CDN edge; editors see changes live within seconds. Reserve dynamic rendering for genuinely per-request pages — search results, personalised views — not for the blog.

This split also keeps costs predictable: the content API is hit at build and revalidation time rather than once per visitor, which matters when it bills per request.

Model content as blocks, render from a registry

Rigid page schemas age badly. The durable approach is a page built from an ordered list of typed blocks — hero, rich text, gallery, call to action — mirrored by a component registry on the Next.js side. Editors compose pages; the frontend maps each block type to a component.

tsx
const registry = {
  hero: HeroBlock,
  richText: RichTextBlock,
  gallery: GalleryBlock,
  cta: CtaBlock,
} as const;

export function BlockRenderer({ blocks }: { blocks: CmsBlock[] }) {
  return blocks.map((block) => {
    const Component = registry[block.type];
    return Component ? <Component key={block.id} {...block} /> : null;
  });
}

Returning null for unknown block types is deliberate: when the CMS gains a block the frontend does not know yet, pages degrade gracefully instead of crashing.

Rich text deserves a renderer too

Long-form fields get the same treatment. Whether the CMS emits Portable Text, structured rich text or HTML, run it through a renderer that maps marks and embedded entries onto your components — internal links become next/link navigations, images become next/image with proper sizing, embedded entries render like any other block. Dumping raw HTML into the page throws all of that away.

Give editors real previews

Editors need to see drafts in the real templates before publishing. Next.js draft mode handles this: a preview URL from the CMS hits a Route Handler that verifies a secret and enables draft mode, after which your data-fetching layer requests draft content and pages render dynamically for that editor only. It is an afternoon of work and it transforms the editorial experience — build it early, not as a retrofit. And render a visible banner while draft mode is active, with a link that exits it; editors forget, then file bugs about content that is not actually live.

Webhooks close the loop

The publish webhook should carry enough context to invalidate precisely: tag content by type and by entry, then revalidate the specific entry tag plus any listing tags it appears in. Verify the webhook signature or a shared secret, and log every invalidation — when an editor reports that the site has not updated, that log answers the question in seconds rather than an afternoon.

Keep the types honest

The CMS schema is a contract, so encode it. Generated types from the CMS or a Zod schema at the fetch boundary both work; what matters is that a content model change breaks your build or your parser loudly, rather than rendering undefined into production markup. Validate at the edge of the system and the rest of the codebase can trust its inputs.

None of these patterns is exotic. Together they produce the outcome that matters: editors who can work independently, and a frontend that stays fast and predictable while they do. Put them in place during the first build — retrofitting them onto a live editorial team is far harder.

Start your project

Have an idea? Let's ship it together.

Tell us what you're building — we'll reply within one business day with an honest take and a clear next step.