Skip to content
Draft
Show file tree
Hide file tree
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
41 changes: 23 additions & 18 deletions documentation/authoring/blueprints/nextjs-app-router.md

Large diffs are not rendered by default.

41 changes: 23 additions & 18 deletions documentation/authoring/blueprints/nextjs-pages-router.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions documentation/concepts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ children:
- ./interaction-tracking-in-web-sdks.md
- ./interaction-tracking-in-node-and-stateless-environments.md
- ./profile-synchronization-between-client-and-server.md
- ./optimization-handoff-and-cache-safe-rendering.md
- ./react-native-sdk-interaction-tracking-mechanics.md
- ./ios-sdk-runtime-and-interaction-mechanics.md
- ./android-sdk-runtime-and-interaction-mechanics.md
Expand Down Expand Up @@ -46,6 +47,9 @@ they are not the first stop for installation or setup commands.
- [Profile synchronization between client and server](./profile-synchronization-between-client-and-server.md) -
explains how profile identity, profile data, cookies, browser storage, and Experience API
responses work together when Node and Web SDK runtimes share a visitor journey.
- [Optimization handoff and cache-safe rendering](./optimization-handoff-and-cache-safe-rendering.md) -
explains how server, static, ISR, and edge-rendered Optimization state reaches the browser, how
cache scopes work, and why request profile state must not enter public caches.
- [React Native SDK interaction tracking mechanics](./react-native-sdk-interaction-tracking-mechanics.md) -
explains how the React Native SDK observes, gates, and emits tracking events, covering event
types, the viewport state machine, default visibility and timing, consent gating, scroll context,
Expand Down

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions documentation/concepts/core-state-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,13 +576,14 @@ provider-owned place to subscribe to `eventStream`, `blockedEventStream`, or oth
soon as SDK state is available and before child router, screen, or entry effects run. Each
subscription still immediately emits its current snapshot.

Both framework roots set up provider-owned SDK instances outside render and render no children while
SDK initialization or provider-managed state subscriber setup is pending. React Web uses
layout-effect scheduling for provider-owned browser SDK creation so ready children normally mount
before first visible paint. React Native keeps async effect scheduling because SDK creation depends
on platform storage and device state. When a framework adapter injects an already-created SDK,
children can render immediately unless `onStatesReady` is provided. React Web also holds children
when `serverOptimizationState` is provided so hydration happens first.
Both framework roots set up provider-owned SDK instances outside render and expose provider-owned
state only after the relevant runtime setup path has started. React Web creates a snapshot runtime
for the first render, then hydrates or creates the live browser SDK in a layout effect so ready
children normally mount before first visible paint. React Native keeps async effect scheduling
because SDK creation depends on platform storage and device state. When a framework adapter injects
an already-created SDK, children can render immediately unless `onStatesReady` is provided. React
Web seeds the initial snapshot from `handoff` when server/static/edge handoff data is provided, so
children can render from that state before the live SDK takes over.

## Diagnostics

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ Apply these constraints before choosing server-only, hybrid, or manual tracking:
views without entry views.
- Browser Insights delivery needs a current Web SDK profile. In direct Web SDK initialization, the
profile can come from `defaults.profile`. In React Web provider handoff and manual Next.js
provider or root setup, pass server-returned Optimization data through `serverOptimizationState`.
In the preferred App Router bound setup, the server root or provider from
`createNextjsAppRouterOptimization()` hands off server data for the browser side automatically. In
Pages Router, pass the server data returned by `getServerSideOptimizationProps()` through
`serverOptimizationState` in `pages/_app.tsx`. The profile can also come from browser-persisted
profile state that persistence consent allows the SDK to load, or a browser Experience API call
such as `page()`, `identify()`, `track()`, or sticky `trackView()`.
provider or root setup, pass server/static/edge Optimization state through the `handoff` prop. In
App Router bound setup, pass the `handoff` returned by `createRequestHandoff()` or
`createHandoffFromSelections()` to the bound root or provider. In Pages Router, pass
`pageProps.contentfulOptimization.handoff` through the bound root or provider in `pages/_app.tsx`.
The profile can also come from browser-persisted profile state that persistence consent allows the
SDK to load, or a browser Experience API call such as `page()`, `identify()`, `track()`, or sticky
`trackView()`.
- Browser storage is best-effort. The Web SDK uses `localStorage` and the `ctfl-opt-aid` cookie when
persistence consent permits continuity; if storage fails or is unavailable, continuity is limited
to in-memory state.
Expand Down Expand Up @@ -406,10 +406,10 @@ delivery. Choose one of these patterns before enabling interaction tracking:

- **Bootstrap the server profile.** For direct Web SDK initialization, serialize the `profile`
returned by the server's `page()` or `identify()` call and pass it as `defaults.profile`. For
React Web and manual Next.js provider or root setup, pass the server `OptimizationData` through
`serverOptimizationState`. Preferred Next.js bound roots and providers hand off server data
automatically. Use this when the same server response already rendered personalized HTML from that
profile.
React Web and manual Next.js provider or root setup, pass a browser `handoff` built from the server
data through the root or provider. Next.js bound roots and providers receive request or selection
handoffs from their framework helper. Use this when the same server response already rendered
personalized HTML from that profile.
- **Re-evaluate in the browser.** Persist `ctfl-opt-aid` on the server, initialize the Web SDK in
the browser, call `page()` after your consent policy allows it, then enable tracking after the
page response populates browser profile state.
Expand All @@ -419,10 +419,10 @@ delivery. Choose one of these patterns before enabling interaction tracking:
Web SDK auto-tracking path.

In Next.js server-rendered integrations, `initialPageEvent="skip"` intentionally avoids the initial
browser Experience API `page()` request when the server already emitted that page event. If that
skip leaves the browser with neither a bound server root or provider handoff nor manual
`serverOptimizationState`, and without a prior persisted browser profile, automatic entry views,
clicks, and hovers cannot deliver until a later browser Experience API call populates profile state.
browser Experience API `page()` request when the server or edge helper already accepted that page
event. If that skip leaves the browser with neither a bound root or provider `handoff` nor manual
handoff, and without a prior persisted browser profile, automatic entry views, clicks, and hovers
cannot deliver until a later browser Experience API call populates profile state.

If the Web SDK must read `ctfl-opt-aid`, do not mark that cookie as `HttpOnly`. Configure `path`,
`domain`, and `SameSite` so the server route and browser code refer to the same profile.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ package setup, use the relevant integration guide or package README.

Each runtime exposes the SDK Experience/event locale through its own API surface:

| Runtime | Locale API surface |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Web** | `new ContentfulOptimization({ locale })`, `optimization.locale`, `optimization.states.locale`, `optimization.setLocale(locale)`, and `<ctfl-optimization-root locale="...">` |
| **React Web** | `<OptimizationRoot locale>`, provider-owned `<OptimizationProvider locale>`, and `useOptimization()` access to `sdk.locale`, `sdk.states.locale`, and `sdk.setLocale(locale)` |
| **Next.js** | Preferred App Router `createNextjsAppRouterOptimization({ locale })` bound components, Pages Router `getServerSideOptimizationProps(context)`, lower-level server `createNextjsOptimization({ locale })`, request-scoped `getNextjsServerOptimizationData(sdk, { locale })`, ESR `getNextjsEsrOptimizationData(sdk, { locale })`, and manual client `OptimizationRoot locale` |
| **Node** | `new ContentfulOptimization({ locale })` for a default, `optimization.forRequest({ locale })` for request scope, and `experienceOptions.locale` as an advanced pass-through when request `locale` is absent |
| **React Native** | `<OptimizationRoot locale>`, provider-owned `<OptimizationProvider locale>`, `ContentfulOptimization.create({ locale })`, `sdk.locale`, and `sdk.setLocale(locale)` |
| **iOS** | `OptimizationConfig(locale:)`, `OptimizationRoot(config:)`, `OptimizationClient.locale`, and `OptimizationClient.setLocale(_:)` |
| **Android** | `OptimizationConfig(locale = ...)`, Compose `OptimizationRoot(config = ...)`, XML Views `OptimizationManager.initialize(config = ...)`, `OptimizationClient.locale`, and `OptimizationClient.setLocale(locale)` |
| Runtime | Locale API surface |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Web** | `new ContentfulOptimization({ locale })`, `optimization.locale`, `optimization.states.locale`, `optimization.setLocale(locale)`, and `<ctfl-optimization-root locale="...">` |
| **React Web** | `<OptimizationRoot locale>`, provider-owned `<OptimizationProvider locale>`, and `useOptimization()` access to `sdk.locale`, `sdk.states.locale`, and `sdk.setLocale(locale)` |
| **Next.js** | App Router `createNextjsAppRouterOptimization({ locale })` bound components and `createRequestHandoff({ locale })`; Pages Router `/pages-router/server` `createRequestHandoff(context, { locale })`; lower-level server `createNextjsOptimization({ locale })` and `getNextjsServerOptimizationData(sdk, { locale })`; edge `createEdgeRequestHandoff({ locale })`; and manual client `OptimizationRoot locale` |
| **Node** | `new ContentfulOptimization({ locale })` for a default, `optimization.forRequest({ locale })` for request scope, and `experienceOptions.locale` as an advanced pass-through when request `locale` is absent |
| **React Native** | `<OptimizationRoot locale>`, provider-owned `<OptimizationProvider locale>`, `ContentfulOptimization.create({ locale })`, `sdk.locale`, and `sdk.setLocale(locale)` |
| **iOS** | `OptimizationConfig(locale:)`, `OptimizationRoot(config:)`, `OptimizationClient.locale`, and `OptimizationClient.setLocale(_:)` |
| **Android** | `OptimizationConfig(locale = ...)`, Compose `OptimizationRoot(config = ...)`, XML Views `OptimizationManager.initialize(config = ...)`, `OptimizationClient.locale`, and `OptimizationClient.setLocale(locale)` |

## The locale channels

Expand Down Expand Up @@ -195,10 +195,7 @@ export const { NextAppAutoPageTracker, OptimizationRoot, OptimizedEntry } =
createNextjsAppRouterOptimization({
clientId,
locale: appLocale,
server: {
enabled: true,
consent,
},
consent: { server: consent },
})
```

Expand All @@ -208,10 +205,10 @@ configuration.

Keep lower-level/manual surfaces for direct request control. `createNextjsOptimization({ locale })`
sets the server SDK default locale, `getNextjsServerOptimizationData(sdk, { locale })` binds an App
Router Server Component request, `getServerSideOptimizationProps(context)` binds a Pages Router
`getServerSideProps` request with explicit `locale` taking precedence over `context.locale`,
`getNextjsEsrOptimizationData(sdk, { locale })` binds a request-rendered ESR flow, and manual client
`OptimizationRoot locale` follows the React Web behavior.
Router Server Component request, the Pages Router server factory's `createRequestHandoff(context, {
locale })` binds a Pages Router `getServerSideProps` request with explicit `locale` taking
precedence over `context.locale`, `createEdgeRequestHandoff({ locale })` binds an edge request, and
manual client `OptimizationRoot locale` follows the React Web behavior.

Next.js manual server runtime (TypeScript):

Expand All @@ -237,12 +234,13 @@ not the current request locale. Manual Server Components pass `headers()` to
`getNextjsServerOptimizationData()` so the SDK can derive page context from the request URL captured
by the Next.js proxy or middleware helper.

Locale handoff is separate from server optimization state handoff. When the browser provider has the
server data at its boundary, pass it with `serverOptimizationState` on `OptimizationRoot`. With App
Router bound components, the bound server root handles that state handoff. With Pages Router,
`pages/_app.tsx` receives the helper result through `pageProps` and passes it to the bound root.
Keep `defaults` for configuration or default state such as consent policy, not for server-returned
profile, selected optimizations, or changes.
Locale selection is separate from browser state handoff. When the browser provider has the
server/static/edge data at its boundary, pass it with the `handoff` prop on `OptimizationRoot`. With
App Router bound components, pass the `handoff` returned by `createRequestHandoff()` or
`createHandoffFromSelections()` to the bound root. With Pages Router, `pages/_app.tsx` receives
`pageProps.contentfulOptimization.handoff` and passes it to the bound root. Keep `defaults` for
configuration or default state such as consent policy, not for server-returned profile, selected
optimizations, or changes.

## Node and stateless SDKs

Expand Down Expand Up @@ -321,10 +319,10 @@ Applications own:
- Passing the Contentful locale to manual CDA and CPA requests or SDK-managed `contentful.js`
fetching.
- Passing the SDK Experience/event locale through top-level SDK `locale`, provider `locale`, Next.js
`createNextjsAppRouterOptimization({ locale })`, Pages Router `getServerSideOptimizationProps()`,
lower-level Next.js `getNextjsServerOptimizationData({ locale })`, Next.js ESR
`getNextjsEsrOptimizationData({ locale })`, native config `locale`, native `setLocale`, or Node
`forRequest({ locale })`.
`createNextjsAppRouterOptimization({ locale })`, App Router and Pages Router
`createRequestHandoff({ locale })`, lower-level Next.js
`getNextjsServerOptimizationData({ locale })`, edge `createEdgeRequestHandoff({ locale })`,
native config `locale`, native `setLocale`, or Node `forRequest({ locale })`.
- Keeping localized content cache keys distinct.
- Refetching Contentful content after locale changes.
- Ensuring the Contentful locale is supported by the target Contentful environment.
Expand Down
Loading
Loading