Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a4dac07
✨ feat(core): Add isomorphic OptimizationRuntime and SnapshotRuntime
TimBeyer Jul 2, 2026
c179f64
✨ feat(web-sdk): Re-export the isomorphic runtime on a `./runtime` su…
TimBeyer Jul 2, 2026
6769047
✨ feat(react-web): Make OptimizationProvider server-side renderable
TimBeyer Jul 2, 2026
e4fe34c
✨ feat(nextjs-sdk_ssr): Seed the provider with server optimization state
TimBeyer Jul 2, 2026
8cae737
📝 docs(concepts): Add server-side rendering and hydration concept
TimBeyer Jul 2, 2026
47eb4a1
♻️ refactor(nextjs-sdk_ssr): Deduplicate server optimization resolution
TimBeyer Jul 2, 2026
6039621
♻️ refactor(nextjs-sdk): Deprecate redundant SSR handoff APIs
TimBeyer Jul 2, 2026
a9ae10c
📝 docs(nextjs): Make serverOptimizationState the documented SSR default
TimBeyer Jul 2, 2026
b6b8e68
📝 docs: Demote deprecated Next.js SSR handoff APIs across guides and …
TimBeyer Jul 2, 2026
d1d094a
🐛 fix(react-web): Render serverOptimizationState at first paint for i…
TimBeyer Jul 2, 2026
fb0daca
🐛 fix(react-web): Keep the injected SDK backing the render when only …
TimBeyer Jul 2, 2026
fb2d870
🐛 fix(web): Render resolved OptimizedEntry variants during SSR
TimBeyer Jul 3, 2026
4aae162
♻️ refactor(nextjs-sdk_ssr): Render entries with the isomorphic Optim…
TimBeyer Jul 3, 2026
a948438
♻️ refactor(nextjs-sdk_hybrid): Migrate off NextjsOptimizationState
TimBeyer Jul 3, 2026
fb3d43b
♻️ refactor(react-web): Drop redundant isReady from provider context
TimBeyer Jul 3, 2026
18c4c2a
♻️ refactor(nextjs-sdk): Delete deprecated NextjsOptimizationState ma…
TimBeyer Jul 3, 2026
3b06c6d
♻️ refactor(nextjs-sdk): Rename ServerOptimizedEntry to ServerOnlyOpt…
TimBeyer Jul 3, 2026
4136108
♻️ refactor(nextjs-sdk_ssr,nextjs-sdk_hybrid): Derive readiness from …
TimBeyer Jul 3, 2026
70aa495
🐛 fix(nextjs-sdk_ssr,nextjs-sdk_hybrid): Resolve SSR variants regardl…
TimBeyer Jul 3, 2026
e73a9e1
🐛 fix(nextjs-sdk_ssr,nextjs-sdk_hybrid): Restore client initial page …
TimBeyer Jul 3, 2026
e47dfc0
🐛 fix(core): Match live SDK consent gating in SnapshotRuntime
TimBeyer Jul 3, 2026
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
5 changes: 5 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
- ./server-side-rendering-and-hydration.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 @@ -45,6 +46,10 @@ 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.
- [Server-side rendering and hydration](./server-side-rendering-and-hydration.md) - explains how the
React layer renders personalized content on the server through an isomorphic runtime seam, where
the server-versus-client boundary falls, and the determinism contract that keeps server HTML and
the first client render identical.
- [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
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ the runtime:
| iOS | `OptimizationClient.resolveOptimizedEntry(baseline:selectedOptimizations:)` | SwiftUI `OptimizedEntry`; UIKit can call the client directly |
| Android | `suspend OptimizationClient.resolveOptimizedEntry(...)` | Compose `OptimizedEntry`; XML Views `OptimizedEntryView` |

Next.js uses the Node server and React Web client surfaces, plus Next.js adapter components such as
`ServerOptimizedEntry` for server-rendered entries.
Next.js uses the Node server and React Web client surfaces. The isomorphic `OptimizedEntry` resolves
entries on the server for first paint and hydrates on the client; `ServerOnlyOptimizedEntry` is
available for pure zero-JavaScript Server Component rendering.

## Inputs and constraints

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ between server and browser, see
Choose the runtime path before designing the event flow. The SDK that renders or observes the
interaction decides which facts are available.

| Path | Runtime responsibility | Use when |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `@contentful/optimization-node` | Bind request consent, locale, profile, and page context; call Experience API methods; resolve entries; emit server-known events. | Server rendering owns personalization, and the event is a request fact or server-observed business action. |
| `@contentful/optimization-web` | Own browser consent state, profile state, storage, automatic DOM observation, browser queues, and Insights delivery. | Non-React or custom browser code needs view, click, hover, route, or manual element tracking after HTML reaches the page. |
| `@contentful/optimization-react-web` | Wrap the Web SDK with React browser providers, hooks, router trackers, and `OptimizedEntry` from `@contentful/optimization-react-web`. | React browser apps need framework-owned state, route page tracking, entry wrappers, or browser-side entry personalization. |
| `@contentful/optimization-nextjs` | Own Next.js adapter surfaces: server helpers and `ServerOptimizedEntry` from `@contentful/optimization-nextjs/server`, request helpers from `@contentful/optimization-nextjs/request-handler`, tracking helpers from `@contentful/optimization-nextjs/tracking-attributes`, and client wrappers from `@contentful/optimization-nextjs/client`. | Next.js apps need server-owned personalization, request and cookie helpers, SSR tracking attributes, and client tracking boundaries. |
| First-party browser collector plus Node SDK | Observe browser interactions in application code, post observations to an app endpoint, validate policy, and call request-bound Node SDK tracking methods. | The browser cannot run the Web SDK, but the app can own DOM observation, payload mapping, profile continuity, and retries. |
| Path | Runtime responsibility | Use when |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `@contentful/optimization-node` | Bind request consent, locale, profile, and page context; call Experience API methods; resolve entries; emit server-known events. | Server rendering owns personalization, and the event is a request fact or server-observed business action. |
| `@contentful/optimization-web` | Own browser consent state, profile state, storage, automatic DOM observation, browser queues, and Insights delivery. | Non-React or custom browser code needs view, click, hover, route, or manual element tracking after HTML reaches the page. |
| `@contentful/optimization-react-web` | Wrap the Web SDK with React browser providers, hooks, router trackers, and `OptimizedEntry` from `@contentful/optimization-react-web`. | React browser apps need framework-owned state, route page tracking, entry wrappers, or browser-side entry personalization. |
| `@contentful/optimization-nextjs` | Own Next.js adapter surfaces: server helpers and `ServerOnlyOptimizedEntry` from `@contentful/optimization-nextjs/server`, request helpers from `@contentful/optimization-nextjs/request-handler`, tracking helpers from `@contentful/optimization-nextjs/tracking-attributes`, and client wrappers from `@contentful/optimization-nextjs/client`. | Next.js apps need server-owned personalization, request and cookie helpers, SSR tracking attributes, and client tracking boundaries. |
| First-party browser collector plus Node SDK | Observe browser interactions in application code, post observations to an app endpoint, validate policy, and call request-bound Node SDK tracking methods. | The browser cannot run the Web SDK, but the app can own DOM observation, payload mapping, profile continuity, and retries. |

## Constraints that decide delivery

Expand All @@ -75,8 +75,9 @@ 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 and Next.js provider handoff, pass
server-returned Optimization data through `serverOptimizationState`. In Next.js page-level
handoff, render `NextjsOptimizationState` under existing SDK context. The profile can also come
server-returned Optimization data through `serverOptimizationState` (call
`hydrateOptimizationData` from `@contentful/optimization-web/bridge-support` inside a Client
Component to hydrate page-specific data under an existing provider). 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
Expand Down Expand Up @@ -312,8 +313,9 @@ of tracking that can only be measured in the browser.
### Render tracking metadata on resolved entries

Use SDK helpers when available instead of copying the attribute map into application code. In
Next.js, `ServerOptimizedEntry` renders the Web SDK tracking attributes from the baseline entry and
the `ResolvedData` returned by `resolveOptimizedEntry()`. For custom SSR wrappers, call
Next.js, the isomorphic `OptimizedEntry` renders the Web SDK tracking attributes as part of
resolving the entry on the server; `ServerOnlyOptimizedEntry` does the same from an explicit
`ResolvedData` for pure zero-JavaScript server rendering. For custom SSR wrappers, call
`getServerTrackingAttributes()` from `@contentful/optimization-nextjs/tracking-attributes`. Non-Next
runtimes can call `resolveOptimizedEntryTrackingAttributes()` from
`@contentful/optimization-web/tracking-attributes` when they already have the same baseline entry
Expand Down Expand Up @@ -393,9 +395,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 Next.js, pass the server `OptimizationData` through `serverOptimizationState`, or
render `NextjsOptimizationState` under an existing SDK context when a Next.js page owns the data.
Use this when the same server response already rendered personalized HTML from that profile.
React Web and Next.js, pass the server `OptimizationData` through `serverOptimizationState` (call
`hydrateOptimizationData` from `@contentful/optimization-web/bridge-support` inside a Client
Component to hydrate page-specific data under an existing provider). 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 @@ -406,9 +409,9 @@ delivery. Choose one of these patterns before enabling interaction tracking:

In Next.js SSR 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 without `serverOptimizationState` or `NextjsOptimizationState`, 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.
the browser without `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.

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 @@ -185,12 +185,13 @@ current request locale. Server Components pass `headers()` to `getNextjsServerOp
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`. When a
shared App Router layout owns the provider and the page owns request-local data, render
`NextjsOptimizationState` near the server-rendered optimized content. Keep `defaults` for
Locale handoff is separate from server optimization state handoff. Pass the server data with
`serverOptimizationState` on `OptimizationRoot`, resolving it in the layout so the provider renders
personalized state on the server and hydrates the same data on the client. Keep `defaults` for
configuration or default state such as consent policy, not for server-returned profile, selected
optimizations, or changes.
optimizations, or changes. (To hydrate page-specific data under an existing provider that was not
seeded with server data, call `hydrateOptimizationData` from
`@contentful/optimization-web/bridge-support` inside a Client Component.)

## Node and stateless SDKs

Expand Down Expand Up @@ -230,9 +231,10 @@ Pass direct single-locale field values to the runtime-specific entry resolution

- Web and Node `resolveOptimizedEntry()`.
- React Web and React Native `OptimizedEntry` and `useEntryResolver()`.
- React Web and Next.js client `useOptimizedEntry()`.
- Next.js server `resolveOptimizedEntry()`; pass the baseline entry and returned `ResolvedData` to
`ServerOptimizedEntry` when server-rendered tracking attributes are needed.
- React Web and Next.js client `useOptimizedEntry()`, and the isomorphic `OptimizedEntry` that
resolves on the server and hydrates on the client.
- Next.js server `resolveOptimizedEntry()` for Server Component resolution;
`ServerOnlyOptimizedEntry` for pure zero-JavaScript server rendering.
- iOS `OptimizationClient.resolveOptimizedEntry(baseline:selectedOptimizations:)` and SwiftUI
`OptimizedEntry(entry:)`.
- Android `OptimizationClient.resolveOptimizedEntry(...)`, Compose `OptimizedEntry(entry:)`, and XML
Expand Down
Loading
Loading