Skip to content

feat(nextjs): add cache-safe optimization handoff support#379

Draft
Charles Hudson (phobetron) wants to merge 1 commit into
mainfrom
NT-3708_implement-ssg-isr-esr-handoff-support
Draft

feat(nextjs): add cache-safe optimization handoff support#379
Charles Hudson (phobetron) wants to merge 1 commit into
mainfrom
NT-3708_implement-ssg-isr-esr-handoff-support

Conversation

@phobetron

@phobetron Charles Hudson (phobetron) commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements explicit, cache-safe Optimization SDK handoff support for Next.js static, ISR, request-time, and edge rendering flows.

This PR adds framework-neutral handoff primitives in Core, browser handoff hydration in Web, React handoff roots, canonical Next.js handoff APIs, an edge-safe Next.js runtime, a Node request handoff helper, updated reference implementations, E2E coverage, and public/internal documentation for the supported rendering modes.

Jira: NT-3708

Why

Optimized content rendering needs to support more than browser-owned runtime state. This PR makes the state handoff explicit for:

  • static routes that render known optimization selections at build time
  • ISR routes that cache public optimization permutations
  • request-time routes that hand private request state to the browser
  • edge routes that own request analytics and profile persistence before browser hydration
  • analytics-only browser hydration when optimized markup has already been rendered outside the browser runtime

The implementation separates:

  • optimization selection state
  • cache-safety metadata
  • browser hydration behavior
  • initial page event ownership
  • profile/private request state
  • customer-owned public permutation state

Review map

Suggested review order:

  1. Core model:

    • packages/universal/core-sdk/src/handoff.ts
    • packages/universal/core-sdk/src/handoff.test.ts
  2. Browser hydration/runtime behavior:

    • packages/web/web-sdk/src/handoff.ts
    • packages/web/web-sdk/src/analytics.ts
    • packages/web/web-sdk/src/handoff-state.ts
    • packages/web/web-sdk/src/*handoff*.test.ts
    • packages/web/web-sdk/src/analytics.test.ts
  3. React Web integration:

    • packages/web/frameworks/react-web-sdk/src/root/OptimizationRoot.tsx
    • packages/web/frameworks/react-web-sdk/src/root/OptimizationAnalyticsRoot.tsx
    • packages/web/frameworks/react-web-sdk/src/provider/OptimizationProvider.tsx
    • related tests under packages/web/frameworks/react-web-sdk/src/**
  4. Next.js public APIs and edge behavior:

    • packages/web/frameworks/nextjs-sdk/src/handoff.ts
    • packages/web/frameworks/nextjs-sdk/src/edge.ts
    • packages/web/frameworks/nextjs-sdk/src/cache-middleware.ts
    • packages/web/frameworks/nextjs-sdk/src/forwarded-request-headers.ts
    • packages/web/frameworks/nextjs-sdk/src/app-router-server.tsx
    • App Router and Pages Router server/client updates
  5. Reference implementations and E2E evidence:

    • implementations/nextjs-sdk_app-router/**
    • implementations/nextjs-sdk_pages-router/**
    • implementations/web-sdk_angular/src/app/services/optimization.ts
    • lib/e2e-web/e2e/handoff.spec.ts
  6. Documentation:

    • documentation/concepts/optimization-handoff-and-cache-safe-rendering.md
    • documentation/guides/rendering-personalized-nextjs-routes-with-static-isr-and-edge-handoffs.md
    • updated Next.js App Router and Pages Router guides
    • documentation/internal/sdk-knowledge/**

Public API changes

Core SDK

Adds @contentful/optimization-core handoff support:

  • OptimizationSelectionState
  • OptimizationCacheScope
  • OptimizationCacheMetadata
  • OptimizationHandoff
  • OptimizationCacheSafetyWarning
  • createSelectionFingerprint
  • createOptimizationCacheKey
  • resolveEntriesForSelections
  • createHandoffFromSelections
  • getOptimizationCacheSafetyWarnings

These APIs let SDKs and customer integrations describe the optimization state used during server/static/edge rendering and attach cache-safety metadata to the rendered output.

Web SDK

Adds browser handoff and analytics-only runtime support:

  • ContentOptimizationHydrationMode
  • OptimizationHydrationMode
  • ContentOptimizationHandoff
  • AnalyticsOptimizationHandoff
  • BrowserOptimizationHandoff
  • hydrateOptimizationHandoff
  • createOptimizationAnalyticsRuntime
  • hydrateOptimizationAnalyticsHandoff

New subpath exports:

  • @contentful/optimization-web/handoff
  • @contentful/optimization-web/analytics

React Web SDK

Adds handoff-aware root support:

  • OptimizationRoot accepts handoff state, hydration behavior, route identity, and initial page event payload builders.
  • OptimizationAnalyticsRoot supports analytics-only hydration.
  • React Web roots accept serializable initialPagePayload for server-framework bridges that cannot pass functions into client components.
  • Tracking attributes can be imported without pulling in the full React runtime.

New subpath exports:

  • @contentful/optimization-react-web/handoff
  • @contentful/optimization-react-web/analytics
  • @contentful/optimization-react-web/tracking-attributes

Next.js SDK

Adds canonical Next.js handoff helpers:

  • createHandoffFromSelections
  • addBrowserHandoffMetadata
  • browser handoff metadata types
  • createNextjsEdgeOptimization
  • createEdgeRequestHandoff
  • createNextjsCacheMiddleware

Adds App Router server support for request-local handoff state so server OptimizedEntry can resolve entries from the active request or selection handoff. Analytics-only handoffs intentionally do not drive server content resolution.

The App Router bound root keeps buildPagePayload as the developer-facing API and converts it to serializable initialPagePayload before crossing the Server Component to Client Component boundary.

Adds the new package subpath:

  • @contentful/optimization-nextjs/edge

Removes the previous package subpath:

  • @contentful/optimization-nextjs/esr

Node SDK

Adds:

  • createRequestHandoffFromData

This lets Node/server integrations create framework-neutral handoff payloads from completed request optimization data.

Rendering strategies covered

Private request handoff

For request-bound rendering where profile state may be private to the visitor.

  • cache scope: private-request
  • may include profile state
  • browser can preserve server-rendered content and hydrate SDK state
  • initial page event can be emitted by server/edge or browser depending on route ownership

Public permutation handoff

For customer-owned cacheable variants such as ISR routes keyed by segment, audience, campaign, or other non-private dimensions.

  • cache scope: public-permutation
  • requires a customer-owned cache key
  • must not include private profile state
  • supports stable cache keys and warning checks

Static handoff

For build-time or otherwise static optimized routes where selections are known without per-visitor private state.

  • cache scope: static
  • must not include private profile state
  • can preserve server-rendered content or use client-only hidden-until-ready hydration

Analytics-only handoff

For routes where optimized markup is already rendered and the browser only needs analytics, profile continuity, and interaction tracking.

  • hydration mode: analytics-only
  • uses OptimizationAnalyticsRoot or the Web SDK analytics runtime
  • does not expose content resolution APIs in the browser runtime
  • does not drive App Router server content resolution

Edge request handoff

For edge routes that resolve optimization state, emit or skip initial page events, persist anonymous IDs, and hand state to the browser.

  • new @contentful/optimization-nextjs/edge entrypoint
  • supports request/header/cookie snapshots
  • supports server consent resolution
  • preserves request context for page events
  • exposes a persist(response) helper for cookie persistence

Breaking and migration notes

/esr was replaced by /edge

@contentful/optimization-nextjs/esr has been removed.

Use:

import { createNextjsEdgeOptimization } from '@contentful/optimization-nextjs/edge'

The new edge API is the supported path for edge-owned request handoff behavior.

Handoff is explicit

Next.js integrations should pass explicit handoff payloads into the React/Web hydration layer instead of relying on implicit browser-only state for server/static/edge-rendered personalized content.

Cache ownership is explicit

Public or static rendered output must not carry private profile state. Public permutation routes should provide a customer-owned cache key. The Core SDK exposes warning helpers to make these cases visible.

App Router server content resolution is handoff-aware

App Router server OptimizedEntry resolves from the active request or selection handoff. This keeps the consumer API centered on the bound SDK components instead of requiring route code to manually thread selected optimizations through every server-rendered entry.

Reference implementation updates

The App Router reference implementation demonstrates:

  • request handoff
  • customer-owned selection handoff
  • analytics-only handoff
  • hidden-until-ready hydration
  • edge request handoff
  • edge selection handoff
  • cache middleware for public permutation routing
  • handoff-aware server OptimizedEntry rendering through the bound App Router SDK components

The Pages Router reference implementation aligns with the handoff contract and shared utility behavior.

The Angular Web SDK reference implementation hydrates server-provided snapshot state through hydrateOptimizationHandoff, keeping it aligned with the Web SDK handoff API instead of using lower-level snapshot hydration directly.

E2E and test coverage

Adds or updates coverage for:

  • deterministic selection fingerprints and cache keys
  • cache-safety warnings
  • framework-neutral handoff creation
  • Node request handoff creation
  • Web handoff hydration
  • analytics-only browser runtime behavior
  • React OptimizationRoot handoff behavior
  • React OptimizationAnalyticsRoot
  • Next.js App Router handoff flow
  • App Router server OptimizedEntry resolution from request and selection handoff state
  • analytics-only handoff isolation from server content resolution
  • Next.js Pages Router handoff flow
  • Next.js edge handoff behavior
  • Next.js middleware request-header forwarding
  • E2E handoff routes in the Next.js reference implementation

Primary new E2E file:

  • lib/e2e-web/e2e/handoff.spec.ts

Documentation updates

Adds:

  • documentation/concepts/optimization-handoff-and-cache-safe-rendering.md
  • documentation/guides/rendering-personalized-nextjs-routes-with-static-isr-and-edge-handoffs.md

Updates:

  • Next.js App Router integration guide
  • Next.js Pages Router integration guide
  • SDK selection guide
  • analytics forwarding guide
  • React Web integration guide
  • related concept docs for state, profile sync, locale, consent, and stateless analytics
  • guide/concept indexes
  • internal SDK knowledge base
  • authoring blueprints and guide authoring checklist
  • package and implementation READMEs

Bundle-size policy

This PR adds bundle budgets for new public entrypoints and removes budgets for removed entrypoints.

Notable changes include:

  • new Web SDK handoff and analytics entrypoint budgets
  • new React Web SDK handoff, analytics, and tracking-attributes entrypoint budgets
  • Next.js budget replacement from esr.* to edge.*
  • updated budgets for affected existing entrypoints after the handoff-capable changes
  • App Router server bundle budgets updated to cover handoff-aware server entry resolution

Bundle budget overruns were treated as validation evidence during implementation. The budget changes reflect the current public entrypoint shape; future budget tightening should be handled as separate optimization work.

Validation

Validation evidence for this branch includes:

  • Next.js App Router E2E: 114 passed, 12 skipped
  • pnpm --filter @contentful/optimization-nextjs typecheck
  • pnpm --filter @contentful/optimization-nextjs test:unit
  • pnpm --filter @contentful/optimization-nextjs build
  • pnpm --filter @contentful/optimization-nextjs size:check
  • pnpm --filter @contentful/optimization-react-web typecheck
  • targeted Prettier checks for touched Next.js and React Web source/test files
  • git diff --check

Additional branch validation performed during the implementation included package, implementation, documentation, and knowledge-base checks documented in the branch work.

Reviewer checklist

Please pay particular attention to:

  • whether the cache scopes and warning behavior make the private/public/static boundary clear
  • whether initialPageEvent: "emit" | "skip" is assigned consistently across server, edge, and browser ownership
  • whether analytics-only handoff behavior avoids exposing content APIs while still preserving tracking needs
  • whether analytics-only handoff state is kept out of App Router server content resolution
  • whether the /edge API shape is the right replacement for the removed /esr subpath
  • whether the App Router and Pages Router examples are realistic enough for customers to copy or adapt
  • whether React Web’s initialPagePayload bridge is clearly an adapter/runtime serialization mechanism while buildPagePayload remains the normal consumer-facing API
  • whether the Angular reference implementation uses the Web SDK handoff API appropriately
  • whether the new guide cleanly separates quick-start integration from advanced static/ISR/edge rendering guidance
  • whether bundle budget changes are reasonable for the current public entrypoint shape

Risk areas

  • The PR changes public API surfaces across Core, Web, React Web, Next.js, and Node packages.
  • The Next.js /esr subpath removal is a breaking package export change.
  • Cache-safe rendering relies on customers choosing the correct cache scope and avoiding private profile state in public/static caches.
  • Initial page event ownership is subtle because request, edge, and browser runtimes can each be responsible depending on the route.
  • App Router Server Component to Client Component boundaries require serializable props, which is why the bound Next.js root evaluates buildPagePayload on the server and passes initialPagePayload to React Web.
  • The reference implementations exercise several rendering modes, so reviewers should check both API ergonomics and route-level behavior.

@phobetron
Charles Hudson (phobetron) force-pushed the NT-3708_implement-ssg-isr-esr-handoff-support branch 2 times, most recently from e07aef3 to 5850191 Compare July 16, 2026 16:59
Add explicit optimization handoff primitives for rendering personalized
Contentful Optimization experiences through static, ISR, request-time, and
edge-owned routes without relying on implicit browser-only state.

Core SDK:
- Add framework-neutral handoff types for selected optimization state,
  managed entry snapshots, and cache metadata.
- Add deterministic selection fingerprints and cache-key helpers.
- Add helpers for resolving baseline entries from customer-owned selections.
- Add cache-safety warnings for public/static handoffs that accidentally carry
  private profile state or omit public permutation cache keys.

Web SDK:
- Add browser-facing content and analytics handoff types.
- Add handoff hydration for preserving server-rendered content or hiding
  client-only content until browser state is ready.
- Add an analytics-only runtime for routes that render optimized markup on the
  server/static/edge side but still need browser analytics and interaction
  tracking.

React Web SDK:
- Add handoff, analytics, and tracking-attributes subpath exports.
- Teach OptimizationRoot to accept handoff state, hydration mode, route keys,
  and initial page event payload builders.
- Add OptimizationAnalyticsRoot for analytics-only hydration.
- Keep tracking attributes available independently from the full React runtime.

Next.js SDK:
- Add canonical handoff helpers for App Router and Pages Router integrations.
- Add the edge entrypoint with createNextjsEdgeOptimization and
  createEdgeRequestHandoff.
- Add cache middleware for customer-owned public permutation routing while
  preserving forwarded Next middleware request headers.
- Update App Router and Pages Router server/client APIs to pass handoff state
  through the public root components.
- Replace the previous /esr export with /edge.

Node SDK:
- Add createRequestHandoffFromData for framework-neutral request handoff
  creation from completed Node request optimization data.

Reference implementations and tests:
- Expand the Next.js App Router implementation with request handoff,
  customer-owned selection handoff, analytics-only, hidden-until-ready, edge
  request, edge selection, and cache middleware routes.
- Refresh the Next.js Pages Router implementation for the new handoff contract.
- Add unit coverage for core, web, React Web, Next.js, and Node handoff behavior.
- Add E2E coverage for the new Next.js handoff routes.

Documentation:
- Add handoff and cache-safe rendering concept documentation.
- Add a dedicated guide for rendering personalized Next.js routes with static,
  ISR, and edge handoffs.
- Refresh Next.js App Router and Pages Router guides to focus the quick-starts
  and route advanced rendering modes to the new guide.
- Update SDK knowledge files, guide indexes, package READMEs, and authoring
  guidance to reflect the new public APIs.

Bundle policy:
- Add budgets for new public entrypoints.
- Remove budgets for removed entrypoints.
- Reset affected budgets to match the current handoff-capable bundle outputs.

BREAKING CHANGE: @contentful/optimization-nextjs/esr is removed. Use
@contentful/optimization-nextjs/edge and the new edge handoff APIs instead.

[[NT-3708](https://contentful.atlassian.net/browse/NT-3708)]
@phobetron
Charles Hudson (phobetron) force-pushed the NT-3708_implement-ssg-isr-esr-handoff-support branch from 5850191 to 51ae699 Compare July 16, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant