Readme · Guides · Reference · Contributing
This reference implementation integrates @contentful/optimization-nextjs with the Next.js App
Router. It demonstrates request-bound personalization, public permutation handoff, analytics-only
rendering, Cache Components routes, and browser takeover without importing lower-level SDKs.
For a complete integration walkthrough, see Integrate the Optimization SDK in a Next.js App Router app. Edge runtime routes live in the Next.js App Router Edge runtime reference implementation.
lib/optimization.ts.lib/optimization-client.ts that runs optional before-initial-page work
before the request root's browser-owned page event.PUBLIC_OPTIMIZATION_ENABLE_PREVIEW_PANEL.proxy.ts re-exports the app's forwarding handler and declares the routes that need Optimization
context:
export { proxy } from './lib/optimization'
export const config = {
matcher: [
'/',
'/page-two',
'/hidden-until-ready',
'/static-shell-private-slot',
'/selection-handoff/:path*',
'/analytics-only/:path*',
],
}
Next.js 13 to 15 uses the same handler from middleware.ts with a middleware export. The linked
guide covers forwarding and trusted handoff options.
Keep public chrome outside Suspense, use a meaningful fallback, and call connection() only at
the private boundary. Managed prefetch overlaps the entry fetch with request initialization:
export default function Page() {
return (
<AppShellChrome>
<Suspense fallback={<PersonalizedContentFallback />}>
<PrivateRequestSlot />
</Suspense>
</AppShellChrome>
)
}
async function PrivateRequestSlot() {
await connection()
return (
<RequestOptimizationRoot prefetchManagedEntries={[entryId]}>
<RequestOptimizedEntry entryId={entryId}>{renderEntry}</RequestOptimizedEntry>
</RequestOptimizationRoot>
)
}
Keep provider-dependent tools inside RequestOptimizationRoot.
lib/optimization-client.ts binds a client-only beforeInitialPage callback. The maintained
callback identifies only when the URL contains ?beforeInitialPage=readiness; ordinary routes do
nothing. lib/optimization.ts injects that module's ClientRequestOptimizationRoot into the server
request family as a Client Component reference.
The server passes only children, defaults, handoff, and hydration through the request root. The
client root derives the current App Router route and lazy page payload in the browser, so neither the
callback nor a payload-builder function crosses the Server Component boundary. It makes the direct
initial page attempt, marks that attempted route through the existing non-emitting initial skip
path, and emits once for each later route. Do not mount a separate request page tracker in the same
subtree.
Use baselineEntry when the app owns Contentful queries and caching. Use entryId when the SDK
binding owns the fetch, and prefetch IDs at the request root when useful:
<RequestOptimizedEntry baselineEntry={entry}>{renderEntry}</RequestOptimizedEntry>
<RequestOptimizationRoot prefetchManagedEntries={[entryId]}>
<RequestOptimizedEntry entryId={entryId}>{renderEntry}</RequestOptimizedEntry>
</RequestOptimizationRoot>
The reference app uses appConfig.locale for SDK configuration, event context, and Contentful CDA
fetches. Keep entries single-locale; don't use withAllLocales or locale=* for SDK entry
resolution. See
Locale handling in the Optimization SDK Suite.
.nvmrc.Run these commands from the monorepo root:
pnpm install
pnpm build:pkgs
pnpm implementation:run -- nextjs-sdk_app-router implementation:install
test -f implementations/nextjs-sdk_app-router/.env || cp implementations/nextjs-sdk_app-router/.env.example implementations/nextjs-sdk_app-router/.env
The .env.example values target the shared local mock API. Replace them only when testing against
live services.
Run the development server on http://localhost:3002:
pnpm implementation:run -- nextjs-sdk_app-router dev
Run local checks from the monorepo root:
pnpm implementation:run -- nextjs-sdk_app-router typecheck
pnpm implementation:run -- nextjs-sdk_app-router lint
pnpm implementation:run -- nextjs-sdk_app-router build
Run the shared Playwright setup and suite:
pnpm setup:e2e:nextjs-sdk_app-router
pnpm test:e2e:nextjs-sdk_app-router