All guidesVercel and env vars
On this page

Next.js error

Route segment config "dynamic" is not compatible with nextConfig.cacheComponents

By Ohad Krispin, founder of VibeRaven · LinkedIn

Delete the export const dynamic line from the page, layout or route handler the error names: with cacheComponents on, Next.js 16 fails the build on it. dynamic = 'force-dynamic' needs no replacement, because under Cache Components uncached data and cookies(), headers() and searchParams already run at request time; if the file reads one of them at the top, move that read into a component wrapped in <Suspense>. dynamic = 'force-static' and export const revalidate become "use cache" with cacheLife, plus cacheTag where you revalidate on demand. The same message names dynamicParams, revalidate, fetchCache and runtime, and new apps hit it because create-next-app turns cacheComponents on by default since Next.js 16.4 (October 6, 2026), so route code written for the previous caching model fails the first build.

Checklist

Replace each rejected export, then rebuild

  1. 01
    List every rejected export in the app. The check runs on page, layout and route files and on metadata files (icon, opengraph-image, sitemap, robots, manifest), and each file gets its own error:
    grep -rnE "export const (dynamic|dynamicParams|revalidate|fetchCache|runtime|experimental_ppr)\b" app src/app 2>/dev/null
  2. 02
    For dynamic = 'force-dynamic' (or revalidate = 0), delete the line. If the file reads cookies(), headers(), searchParams or an uncached fetch at the top, move that read into a child component inside <Suspense>, so the rest of the page prerenders and that part streams in at request time:
    // Before: app/dashboard/page.tsx
    import { cookies } from 'next/headers'
    
    export const dynamic = 'force-dynamic'
    
    export default async function Page() {
      const theme = (await cookies()).get('theme')?.value
      return <Dashboard theme={theme} />
    }
    
    // After
    import { Suspense } from 'react'
    import { cookies } from 'next/headers'
    
    export default function Page() {
      return (
        <Suspense fallback={<p>Loading...</p>}>
          <Dashboard />
        </Suspense>
      )
    }
    
    async function Dashboard() {
      const theme = (await cookies()).get('theme')?.value
      // ...
    }
  3. 03If the route has no useful static part, such as a page that checks the session in its layout before rendering anything, you can instead add export const instant = false to that page or layout. It lets the segment block on request-time data; it does not turn off prerendering, and the Next.js docs ask you to prefer <Suspense> where you can.
  4. 04
    For dynamic = 'force-static' or a revalidate number, delete the line, put "use cache" in the page or in a data function, and set the lifetime with cacheLife ('seconds', 'minutes', 'hours', 'days', 'weeks' or 'max'). Tags from fetch next.tags move to cacheTag, and revalidateTag now takes a profile as its second argument:
    // Before
    export const revalidate = 3600
    
    export default async function Page() {
      const posts = await fetch('https://api.example.com/posts').then((res) => res.json())
      return <PostList posts={posts} />
    }
    
    // After
    import { cacheLife, cacheTag } from 'next/cache'
    
    export default async function Page() {
      'use cache'
      cacheLife('hours')
      cacheTag('posts')
      const posts = await fetch('https://api.example.com/posts').then((res) => res.json())
      return <PostList posts={posts} />
    }
    
    // In a Route Handler or webhook: revalidateTag('posts', 'max')
  5. 05force-static also made cookies() and headers() return empty values. Without it they return the real request values, so remove those reads from a route that must stay prerendered. On Next.js 16.4 and later, export const ensureStatic = 'navigation' fails the build if request-time work gets into that route again.
  6. 06For fetchCache, delete the line. With 'force-cache', 'default-cache' or 'only-cache', wrap the data that should stay cached in "use cache"; with the no-store values, leave the data outside any cache scope, since fetches are uncached by default under Cache Components.
  7. 07For dynamicParams, delete the line; if it was false, call notFound() when a param does not match real data. generateStaticParams must return at least one param under Cache Components. For runtime, delete the line even when it says 'nodejs', because Cache Components requires the Node.js runtime; move edge-only logic to proxy.
  8. 08Run next build again. Removing force-dynamic often leads to the next error, a page that reads cookies(), headers() or searchParams outside <Suspense>; next build --debug-prerender prints the file and line.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

I deleted it from the page. Why does the build still fail?

Another file in the same route exports it: a layout.tsx above the page, a route.ts handler, or a metadata file such as opengraph-image.tsx or sitemap.ts. Next.js checks each file on its own, so search the whole app folder, not only the page the error named first.

Can I keep export const dynamic and turn cacheComponents off instead?

Yes. Remove cacheComponents from next.config (and partialPrefetching, which create-next-app 16.4 sets with it), and the previous caching model and its segment configs work again; create-next-app also takes --no-cache-components. The Next.js 16.4 post says Cache Components becomes the default in Next.js 17, so migrating the exports now avoids doing it at the next major upgrade.

What replaces dynamic = 'force-static' in a GET route handler?

Delete the export and move the data access into a separate function marked "use cache" with a cacheLife, then call it from GET. The directive cannot go on the GET export itself. A GET handler that reads no request or uncached data prerenders on its own.

Does "use cache" keep data across deployments like fetch caching did?

Not by default. "use cache" stores entries in memory, scoped to one deployment and dropped when the serverless instance is destroyed. The fetch Data Cache and unstable_cache persisted across deployments. For a shared, persistent cache, use "use cache: remote" or a cache handler.