What Next.js 16 Changed for CMS-Driven Pages
Next.js 16 shipped on October 21, 2025, and it quietly rewrote the deal between a headless CMS and the App Router cache. revalidateTag() now wants a second argument, a cache profile such as 'max'. The one-argument form still runs, but it is deprecated and may be removed in a later release. Caching is also opt-in under the new Cache Components model, and draftMode() must be awaited.
Those three changes decide how well a CMS fits an App Router project today. A platform with an up-to-date Next.js package handles them for you, while a platform you reach through plain fetch calls works too, but your team writes the webhook handler, the preview route and the cache tags by hand.
We compared eight platforms on that basis: Sanity, Payload, Storyblok, Prismic, Contentful, DatoCMS, Strapi and Builder.io. Every price below is the vendor’s own list price in US dollars, except DatoCMS, which prices in euros.

The revalidateTag change matters most for CMS sites, because it decides what a visitor sees in the seconds after an editor publishes. With 'max', a visitor gets the cached page at once while Next.js fetches fresh content in the background. If you need content gone the moment an editor hits publish, pass { expire: 0 } instead. The newer updateTag() gives read-your-writes behavior, but it only runs inside Server Actions, so a CMS webhook cannot call it.
Two smaller changes trip people up during upgrades, because middleware.ts is now proxy.ts and Node.js 20.9 is the new minimum version. Check both before you blame your CMS package for a failed build.
App Router Support Across Eight Headless CMS Platforms
We read each vendor’s Next.js guide and pricing page and logged the facts that shape day-one work. “Native package” means the vendor publishes and documents its own App Router integration.
| Platform | App Router package | Preview approach | Free plan | Paid entry price |
|---|---|---|---|---|
| Sanity | next-sanity (v13+ for Live) | Live Content API with defineLive, draft mode | 20 seats, 10k documents | Growth, $15 per seat a month |
| Payload | @payloadcms/next, runs inside your app | Draft and live preview in the admin | Open source, MIT | No license fee |
| Storyblok | @storyblok/react/rsc | Visual Editor bridge | 1 seat (up to 2), 100k API requests | Growth, $99 a month ($90.75 billed yearly) |
| Prismic | @prismicio/next | Preview route plus Page Builder | 1 user, 4 million API calls | Starter, $10 a month per repository, billed yearly |
| Contentful | @contentful/live-preview (React) | Live preview in a client component | 10 users, 100K API calls a month | Lite, $300 a month |
| DatoCMS | react-datocms | Real-time updates API | 300 records, 100k API calls | Professional, €149 a month billed yearly |
| Strapi | No Next.js package; REST or GraphQL | Draft and publish, your own preview route | Self-hosted, MIT | Strapi Cloud Starter, $35 a month |
| Builder.io | @builder.io/sdk-react | Visual editor | 1 user, 60 credits a month | Pro, $24 per user a month |
Platform Notes for App Router Teams
Sanity on the App Router

Sanity ships dedicated App Router tooling for teams that want live updates. Its defineLive helper returns two things: sanityFetch for Server Components and a <SanityLive> component that goes in the root layout. Every response carries sync tags, and the browser listens to an event stream and refetches queries whose tags change. Editors see their changes on the page without waiting for a webhook round trip.
The Live features need next-sanity 13 or later, Next.js 16 and React 19.2.3 or later. If you embed Sanity Studio inside the same app, keep <SanityLive> out of the studio route, where it can cause unexpected reloads.
The Free plan allows up to 20 seats but only two roles, Administrator and Viewer. Growth costs $15 per seat a month, adds Editor, Developer and Contributor roles, and raises documents to 25k. Five editors on Growth come to $900 a year at list price.
Payload on the App Router

Payload takes a different route, since it installs straight into your Next.js /app folder, in a (payload) route group copied from the Blank Template. Your own pages sit in a second route group, such as (my-app). The admin panel, the API and the website then ship as one deployment on one host.
Payload is MIT-licensed, so there is no seat fee, and you pay for hosting and a database: MongoDB, Postgres or SQLite through official adapters. Payload announced on June 17, 2025 that it was joining Figma, and the team said Payload remains open source with the self-hosted option intact.
Version pinning matters here, because Payload 3 lists specific Next.js ranges, such as 15.4.11 to 15.4.x or 16.2.6 and later, and warns that not every Next.js 15 or 16 release works. Read that compatibility table before running npm update on a live project.
Storyblok on the App Router

Storyblok is built around a visual editor where marketers click on a block in the live page and edit it. For the App Router you import from @storyblok/react/rsc, initialise once with storyblokInit, and render nested blocks with StoryblokServerComponent. The current guide was tested with Next.js 16.2.1 and React 19.2.4, so it reflects the async APIs of Next.js 16.
One setting catches many new users: if your space was not created in the EU region, you must pass the right region in apiOptions, or API calls fail.
The free Starter plan covers one seat (two at most) and 100,000 API requests a month. Growth is $99 a month, or $90.75 a month billed yearly, with five seats included and room for ten at $15 per extra seat.
Prismic on the App Router
Prismic organises pages as slices, reusable sections that developers build as React components and editors stack in any order. Its @prismicio/next package handles previews, and the Free plan includes unlimited documents and 4 million API calls a month for one user.
Prismic charges per repository, billed yearly: Starter is $10 a month for 3 users, Small is $25 for 7, Medium is $150 for 25 and Platinum is $675 with unlimited users. For a five-person content team, Small at $300 a year is the lowest bill in our table among hosted platforms with paid seats.
Contentful on the App Router

Contentful fits teams that already model content across several channels, and its live updates come from the @contentful/live-preview package. The React hook useContentfulLiveUpdates must run in a client component, so on the App Router you wrap the server tree in a small client provider.
The Free plan allows 10 users, two locales and 100K API calls a month with hard caps. The next step up, Lite, costs $300 a month. Salesforce announced a definitive agreement to acquire Contentful in June 2026 and said it would keep Contentful’s composable, API-first design.
DatoCMS on the App Router

DatoCMS is GraphQL-first, with a React toolkit called react-datocms that covers responsive images and real-time updates. Its free plan is small: 300 records and 100k API calls a month, with no overages allowed. Professional starts at €149 a month billed yearly, or €199 billed monthly, and includes 10 collaborators, 100k records and 1TB of traffic.
Strapi on the App Router
Strapi is MIT-licensed and free to self-host, and because there is no dedicated App Router package, you call its REST or GraphQL API with fetch and add cache tags yourself. That is less work than it sounds, and the pattern in the next section covers it.
Strapi Cloud prices per project: Starter $35 a month with 100,000 API requests, Pro $90 with 1,000,000, and Business $450 with 10,000,000. Overages run $1.50 per extra 25,000 requests, and the old Cloud free plan has been discontinued.
Builder.io on the App Router

Builder.io gives marketers a drag-and-drop editor that renders your own React components. Its Gen 2 package for React and Next.js is @builder.io/sdk-react. Pricing is per user and credit-based: Free with 60 credits a month, Pro at $24 per user a month, and Team at $40 per user a month for up to 20 users.
Price Points for a Five-Editor Next.js Site
Five people editing is a common size for a startup marketing site. We priced each platform for that team over 12 months using list prices, before tax and add-ons. Free plans are shown only when five users fit inside them without breaking a stated limit.
| Platform and plan | How we counted | Yearly cost |
|---|---|---|
| Payload (self-hosted) | MIT license, hosting not included | $0 license |
| Strapi (self-hosted) | MIT license, hosting not included | $0 license |
| Contentful Free | 10 users allowed, 100K API calls a month cap | $0 |
| Prismic Small | $25 a month per repository, 7 users | $300 |
| Strapi Cloud Starter | $35 a month per project | $420 |
| Sanity Growth | $15 x 5 seats x 12 | $900 |
| Storyblok Growth | $90.75 a month billed yearly, 5 seats included | $1,089 |
| Builder.io Pro | $24 x 5 users x 12 | $1,440 |
| DatoCMS Professional | €149 a month billed yearly, 10 collaborators | €1,788 |
| Contentful Lite | $300 a month | $3,600 |
Two things in that table deserve a second look before anyone signs a contract. Self-hosted options carry no license fee, yet you still pay for the server, the database, backups and upgrades. And Sanity’s Free plan technically fits five people, but with only Administrator and Viewer roles, every editor would be an admin. Most teams move to Growth for that reason alone, even when the seat count would fit the free plan.
Cache Components and CMS Data in Next.js 16
Next.js 16 makes caching opt-in through Cache Components, which you turn on with cacheComponents: true in next.config.ts. Once it is on, nothing is cached unless you mark it, so a CMS-backed page that forgot its directive will call the CMS API on every request and quietly spend your monthly quota.
The fix is to wrap CMS reads in a function that carries the 'use cache' directive and attach a tag with cacheTag, which the webhook route can then target with revalidateTag:
import { cacheTag } from 'next/cache'
export async function getPosts() {
'use cache'
cacheTag('posts')
const res = await fetch(`${process.env.CMS_URL}/api/posts`)
return res.json()
}
Draft mode and Cache Components work together in a way that suits editors. When draft mode is on, every function and component under a caching directive runs again on each request and nothing is saved to the cache, so editors always see fresh drafts while visitors keep the cached version. You can read isEnabled inside a cached scope, but cookies() and headers() are not allowed there, and calling enable() or disable() inside one throws an error.
Tags have limits of their own, since they are case-sensitive and must not exceed 256 characters, and a longer tag is never assigned, so revalidating it does nothing. Short tags such as posts, post:my-slug or a document type name are safer than full URLs.
A Webhook-to-revalidateTag Pattern for Any CMS
Every platform in this list can send a webhook when content is published. The App Router side looks the same for all of them, whichever vendor sends the request. First, tag the fetch that reads CMS content so Next.js knows which cache entries belong to it:
// app/blog/[slug]/page.tsx
const res = await fetch(`${process.env.CMS_URL}/api/posts?slug=${slug}`, {
next: { tags: ['posts'] },
})
Then expose a Route Handler that the CMS can call whenever an editor publishes. The shape below follows the Next.js reference example, with a shared secret added so strangers cannot flush your cache:
// app/api/revalidate/route.ts
import type { NextRequest } from 'next/server'
import { revalidateTag } from 'next/cache'
export async function POST(request: NextRequest) {
if (request.headers.get('x-webhook-secret') !== process.env.REVALIDATE_SECRET) {
return Response.json({ revalidated: false }, { status: 401 })
}
const tag = request.nextUrl.searchParams.get('tag')
if (!tag) {
return Response.json({ revalidated: false, message: 'Missing tag' }, { status: 400 })
}
revalidateTag(tag, 'max')
return Response.json({ revalidated: true, now: Date.now() })
}
In the CMS, point the publish webhook at /api/revalidate?tag=posts and add the secret as a custom header. Sanity’s own handler goes one step further, because it uses parseBody from next-sanity/webhook to verify a signed payload and revalidates by document type. Store webhook secrets with the rest of your production credentials, and rotate them when staff leave, as you would any shared secret on a small-business security checklist.
A quick way to test the route before wiring the CMS is to send the POST from an API client. Our guide to API clients that replace Bruno lists free tools that store requests like this in Git.
Draft Mode Wiring for Editor Previews
Draft mode sets a bypass cookie so editors see unpublished content while everyone else gets the cache. In Next.js 16 the function is async, so it must be awaited:
// app/api/draft/route.ts
import { draftMode } from 'next/headers'
export async function GET(request: Request) {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode is enabled')
}
Pages then read const { isEnabled } = await draftMode() and ask the CMS for draft content when it is true, while in production check a secret token in that route before enabling the cookie, and redirect to the page being previewed. Next.js generates a new bypass cookie value on every next build, so it cannot be guessed. If you link to the draft route with <Link>, pass prefetch={false}, or a prefetch can delete the cookie.
Payload, Storyblok, Prismic and Sanity ship this route for you in their starters. With Strapi and Contentful you usually write it yourself, which takes about 20 lines.
Choosing a CMS for Your App Router Project
We would start from who edits the site and how they like to work, then check the money.
- Developers own the content model and want one deploy: Payload, because the CMS lives in your repository and inside your Next.js app.
- Editors want instant updates without webhooks: Sanity with
defineLive. - Marketers build landing pages by clicking on the page: Storyblok or Builder.io, both with visual editors that render your components.
- Page sections repeat across a large marketing site: Prismic slices keep design consistent and keep costs low for small teams.
- Content feeds apps, email and web at once: Contentful or DatoCMS, with structured models and locales.
- You want open source, a separate API server and full data control: Strapi, self-hosted or on Strapi Cloud.
Budget for the agency or freelancer time as well, because wiring previews, webhooks and components often takes longer than the CMS subscription costs in a year. Our freelance hourly rate calculator is a quick way to price that work. If you plan to sell the CMS as part of a client package, the SaaS pricing models explainer covers per-seat and usage pricing in more depth.
Teams on other frameworks have different options, so we cover headless CMS platforms for SvelteKit separately, and our guide to choosing a CMS for any React site compares costs at 3, 10 and 25 editors.
Next.js and Headless CMS Questions
Does Every Headless CMS Work With React Server Components?
Yes, at the data level, since a Server Component can call any CMS API with fetch. The differences show up in previews and visual editing, which often need a small client component, as with Contentful’s live updates hook.
Should I Use revalidateTag or Time-Based Revalidation?
Use tags when the CMS can send a publish webhook, because pages then refresh only when content actually changes. Time-based revalidation is a fallback for sources that cannot call your site.
Can I Run Payload and My Website on the Same Vercel Project?
Yes. Payload installs into the same /app folder, and your site lives in a separate route group. You still need a hosted database, such as Postgres or MongoDB, because Payload stores content there.
Which CMS Has the Most Generous Free Plan for Next.js?
It depends on the limit you hit first, because each plan caps something different. Sanity allows up to 20 seats, Contentful allows 10 users, and Prismic allows 4 million API calls but only one user. Payload and Strapi are free to self-host if you are happy to run your own server and database.
Docs and Pricing Pages Behind This Comparison
- Next.js 16 release notes
- Next.js revalidateTag reference
- Next.js draftMode reference
- Sanity live content guide for Next.js
- Sanity webhook validation for Next.js
- Sanity pricing
- Payload installation docs
- Payload is joining Figma
- Storyblok Next.js guide
- Storyblok pricing
- Prismic plans and pricing
- Contentful live updates docs
- Contentful pricing
- Salesforce agreement to acquire Contentful
- DatoCMS pricing
- Strapi Cloud pricing
- Strapi Cloud usage and billing
- Strapi self-hosting and license
- Builder.io SDK comparison
- Builder.io pricing





