Use this guide when a Next.js App Router app carries legacy Next.js, ESR, SSR plugin, or React
experience.js wiring and you want to move server rendering to
@contentful/optimization-nextjs/app-router/server, with
@contentful/optimization-nextjs/app-router/client only for bound Client Components.
The App Router server binding provides a nested optimization.request component family for request
context, server first paint, route tracking, entry resolution, and browser handoff. A separate client
binding supports bound Client Components. Legacy Next provider, tracker, SSR plugin, ESR helper,
React component, and plugin behavior should be replaced by these App Router surfaces plus the shared
migration guides.
Start with the Next.js App Router integration guide.
Gather these inputs:
@ninetailed/experience.js-next, @ninetailed/experience.js-next-esr, SSR plugin, and
React imports.ntaid or browser legacy storage for profile continuity.OptimizedEntry.Do not carry forward package-root or ESR helper assumptions. The target App Router import path is
@contentful/optimization-nextjs/app-router/server for Server Components. Use
@contentful/optimization-nextjs/app-router/client only for bound Client Components and
@contentful/optimization-nextjs/client for browser hooks and per-entry controls. Legacy ESR
middleware and selector files are not supported public import surfaces.
Remove legacy tracker and provider wiring before adding optimization.request.OptimizationRoot and
optimization.request.NextAppAutoPageTracker, so the request family owns server state handoff and
browser tracking once.
Create one server binding with bindNextjsAppRouterServerOptimization from
@contentful/optimization-nextjs/app-router/server, then use its nested optimization.request
components for ordinary per-visitor routes. Configure the no-argument
createNextjsOptimizationContextHandler() from @contentful/optimization-nextjs/request-handler as
shown in
Request context and the profile cookie:
Next.js 16 uses proxy.ts with a proxy export, while Next.js 13 to 15 use middleware.ts with a
middleware export. The default handler only forwards sanitized request context and the original
request URL; the request family performs request evaluation.
If the handler is missing or misnamed, the request family reports a missing forwarded request URL. Verify the handler by loading a matched route and confirming that error is absent before replacing many render surfaces.
Move profile continuity to the App Router request context and target SDK cookie behavior. The target
profile cookie is ctfl-opt-aid; it must be browser-readable so browser takeover can continue the
same visitor. The app still owns the consent record and the server consent resolver.
Every optimization.request wrapper shares one SDK-owned initializer for the active request. It
derives the request URL, route key, page payload, hydration mode, and handoff once. Mount
optimization.request.NextAppAutoPageTracker inside optimization.request.OptimizationRoot; the
tracker receives first-page-event ownership from that shared handoff automatically. Do not create or
pass app-owned handoff, route-key, page-payload, or initialPageEvent plumbing for the ordinary
request-family path.
Replace legacy mapped experiences and React wrappers with
optimization.request.OptimizedEntry. Use baselineEntry when your app fetched the entry manually.
Use entryId only when the binding config includes contentful for managed server fetching. If the
browser must continue that managed entry, pass matching descriptors through
optimization.request.OptimizationRoot's prefetchManagedEntries prop. The request family fetches
and merges those entries into its handoff. The server component resolves against the current
request's selected optimizations and returns the variant or baseline.
Server personalization reads request data and makes the route dynamic. Do not keep ISR or static assumptions on routes that render request-specific personalized output.
Bound Client Components use a separate binding from
@contentful/optimization-nextjs/app-router/client. Client-side flags, analytics forwarding,
preview, and live updates use the React Web runtime behind the App Router SDK:
Verify server HTML, hydration, and browser takeover together:
optimization.request root, entry, and tracker components.@ninetailed/experience.js-next, @ninetailed/experience.js-next-esr, and
legacy React imports.| Symptom | Check |
|---|---|
| Request components report a missing request URL | Confirm the request handler file, export, and route matcher for your Next.js version. |
| The route conflicts with static generation | Request-family personalization is dynamic; use a public-permutation, static, or browser-only path when required. |
| Hydration changes a managed entry | Prefetch the matching descriptor through the request root and keep the browser on the same component path. |
| Duplicate page events appear on a request-family path | Mount the request-family root and tracker together; remove app-owned handoff and initialPageEvent tracker props. |