Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 103 additions & 6 deletions docs/01-app/02-guides/migrating-to-cache-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ nav_title: Migrating to Cache Components
description: Learn how to migrate from route segment configs to Cache Components in Next.js.
---

When [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled, route segment configs like `dynamic`, `revalidate`, and `fetchCache` are replaced by [`use cache`](/docs/app/api-reference/directives/use-cache) and [`cacheLife`](/docs/app/api-reference/functions/cacheLife).
When [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled, use [`use cache`](/docs/app/api-reference/directives/use-cache) and [`cacheLife`](/docs/app/api-reference/functions/cacheLife) instead of route segment configs like `dynamic`, `revalidate`, and `fetchCache`.

## `dynamic = "force-dynamic"`

**Not needed.** All pages are dynamic by default.
**Not needed.** Remove `export const dynamic = 'force-dynamic'`. When Cache Components is enabled, Next.js prerenders static segments and handles dynamic work during the request. The `force-dynamic` flag does not change that rendering path.

```tsx filename="app/page.tsx" switcher
// Before - No longer needed
Expand All @@ -29,22 +29,22 @@ export default function Page() {
```

```tsx filename="app/page.tsx" switcher
// After - Just remove it
// After - Remove the export
export default function Page() {
return <div>...</div>
}
```

```jsx filename="app/page.js" switcher
// After - Just remove it
// After - Remove the export
export default function Page() {
return <div>...</div>
}
```

## `dynamic = "force-static"`

Start by removing it. When unhandled uncached or runtime data access is detected during development and build time, Next.js raises an error. Otherwise, the prerendering step automatically extracts the static HTML shell.
**Start by removing it.** When unhandled uncached or runtime data access is detected during development and build time, Next.js raises an error. Otherwise, the prerendering step automatically extracts the static HTML shell.

For uncached data access, add [`use cache`](/docs/app/api-reference/directives/use-cache) as close to the data access as possible with a long [`cacheLife`](/docs/app/api-reference/functions/cacheLife) like `'max'` to maintain cached behavior. If needed, add it at the top of the page or layout.

Expand Down Expand Up @@ -96,7 +96,7 @@ export default async function Page() {

## `revalidate`

**Replace with `cacheLife`.** Use the `cacheLife` function to define cache duration instead of the route segment config.
**Replace with `cacheLife`.** Use the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) function with a [preset profile](/docs/app/api-reference/functions/cacheLife#using-preset-profiles) instead of the route segment config. For example, `revalidate = 3600` corresponds to the `'hours'` profile.

```tsx filename="app/page.tsx" switcher
// Before
Expand Down Expand Up @@ -170,6 +170,103 @@ export default async function Page() {
}
```

## `generateStaticParams`

[`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) works with [dynamic routes](/docs/app/api-reference/file-conventions/dynamic-routes) when Cache Components is enabled, but validation is stricter and the `Route (app)` section from `next build` uses different symbols.

With Cache Components, returning `[]` from `generateStaticParams` triggers a [build error](/docs/messages/empty-generate-static-params). The build needs at least one path to validate the prerendered shell. Return at least one param, or remove `generateStaticParams` and let params resolve at request time (see [dynamic routes with Cache Components](/docs/app/api-reference/file-conventions/dynamic-routes#with-cache-components)).

```tsx filename="app/blog/[slug]/page.tsx" switcher
// Before
export async function generateStaticParams() {
return []
}

export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <div>{slug}</div>
}
```

```jsx filename="app/blog/[slug]/page.js" switcher
// Before
export async function generateStaticParams() {
return []
}

export default async function Page({ params }) {
const { slug } = await params
return <div>{slug}</div>
}
```

```tsx filename="app/blog/[slug]/page.tsx" switcher
// After - Return at least one param (expand with your real slugs)
export async function generateStaticParams() {
return [{ slug: 'hello-world' }]
}

export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <div>{slug}</div>
}
```

```jsx filename="app/blog/[slug]/page.js" switcher
// After - Return at least one param (expand with your real slugs)
export async function generateStaticParams() {
return [{ slug: 'hello-world' }]
}

export default async function Page({ params }) {
const { slug } = await params
return <div>{slug}</div>
}
```

> **Good to know**: Params not returned by `generateStaticParams` are served with the prerendered shell while dynamic content streams in, and the result is cached for subsequent visits. This is the default behavior in Next.js 16.3+. In earlier versions, unlisted params wait for a full server render before the response is sent.
Copy link
Copy Markdown
Member Author

@aurorascharff aurorascharff Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@icyJoseph @ztanner this is with partialfallbacks in mind
Let me know if this is an okay framing


The `Route (app)` section from `next build` also changes. With Cache Components, **`●` (SSG)** is replaced by **`○` (Static)** or **`◐` (Partial Prerender)** depending on whether the route contains dynamic content:

**Before Cache Components:**

```bash filename="Terminal"
Route (app)
┌ ○ /
├ ○ /_not-found
└ /products/[id]
├ ● /products/1 # Prerendered via generateStaticParams
└ ● /products/2 # Prerendered via generateStaticParams

○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses generateStaticParams)
```

**With Cache Components:**

```bash filename="Terminal"
Route (app)
┌ ○ /
├ ○ /_not-found
└ /products/[id]
├ ◐ /products/[id] # Shell for unlisted params
├ ○ /products/1 # Fully prerendered
└ ○ /products/2 # Fully prerendered

○ (Static) prerendered as static content
◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed content
```

See [Building](/docs/app/guides/building) for more on `next build` output and symbols. See [`generateStaticParams` with Cache Components](/docs/app/api-reference/functions/generate-static-params#with-cache-components) for Suspense boundaries, placeholder params, and the full contract.

## `runtime = 'edge'`

**Not supported.** Cache Components requires the Node.js runtime. Switch to the Node.js runtime (the default) by removing the `runtime = 'edge'` export. If you need edge behavior for specific routes, use [Proxy](/docs/app/api-reference/file-conventions/proxy) instead.
Loading