Readme · Guides · Reference · Contributing
The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.
Reference implementation demonstrating @contentful/optimization-react-web usage in a React SPA.
This is the primary React Web reference implementation for customer-style usage of the official
React framework package.
This implementation is the React Web SDK counterpart to
web-sdk_react. Where web-sdk_react builds its own React adapter
layer over @contentful/optimization-web, this implementation uses the official
@contentful/optimization-react-web framework package directly to match customer integration
code. There is no src/optimization/ adapter directory.
| Feature | SDK surface used |
|---|---|
| Provider + initialization | OptimizationRoot |
| SPA page tracking | ReactRouterAutoPageTracker from @contentful/optimization-react-web/router/react-router |
| Entry resolution + rendering | OptimizedEntry render prop |
| Live updates (global) | OptimizationRoot liveUpdates prop |
| Live updates (per-component) | OptimizedEntry liveUpdates prop |
| Live updates (locked) | <OptimizedEntry liveUpdates={false}> |
| Merge tag rendering | useMergeTagResolver().getMergeTagValue() |
| Nested personalization | Nested <OptimizedEntry> composition |
| Consent gating | sdk.consent() via useOptimizationContext() |
| Identify / reset | sdk.identify() / sdk.reset() via useOptimizationContext() |
| Auto view/click/hover | Default OptimizationRoot observers + OptimizedEntry tracking props |
| Manual view tracking | <OptimizedEntry trackViews={false}> + sdk.tracking.enableElement() |
| Flag view tracking | sdk.states.flag('boolean').subscribe() |
| Analytics event stream | sdk.states.eventStream.subscribe() |
| Preview panel attachment | Env-gated attachOptimizationPreviewPanel() call |
| Offline queue / recovery | Inherited from @contentful/optimization-web runtime |
This app defines one APP_LOCALE, passes it through the provider locale prop, and passes it
directly to Contentful CDA entry fetches. Do not use contentful.js withAllLocales or raw CDA
locale=* for entries passed to OptimizedEntry; SDK entry resolution expects direct single-locale
fields such as fields.nt_experiences and fields.nt_variants. See
Locale handling in the Optimization SDK Suite
for the broader locale model and
Entry personalization and variant resolution
for the entry contract.
.nvmrc)From the repository root:
pnpm install
pnpm build:pkgs
pnpm implementation:run -- react-web-sdk implementation:install
test -f implementations/react-web-sdk/.env || cp implementations/react-web-sdk/.env.example implementations/react-web-sdk/.env
From the repository root:
pnpm serve:mocks
pnpm implementation:run -- react-web-sdk dev
pnpm implementation:run -- react-web-sdk build
pnpm implementation:run -- react-web-sdk typecheck
The equivalent implementation-directory commands are:
pnpm dev
pnpm build
pnpm typecheck
pnpm --dir lib/e2e-web setup:e2e
pnpm test:e2e:react-web-sdk
pnpm implementation:run -- react-web-sdk serve
In another terminal:
IMPLEMENTATION=react-web-sdk pnpm --dir lib/e2e-web test
When finished:
pnpm implementation:run -- react-web-sdk serve:stop
This implementation uses the shared Playwright suite from
lib/e2e-web. The implementation sets IMPLEMENTATION=react-web-sdk
when invoking that suite.
Use Playwright UI or codegen when needed:
pnpm implementation:run -- react-web-sdk test:e2e:ui
pnpm implementation:run -- react-web-sdk test:e2e:codegen
The setup step creates the local .env file if needed:
test -f implementations/react-web-sdk/.env || cp implementations/react-web-sdk/.env.example implementations/react-web-sdk/.env
All variables have mock-safe defaults. To use local mock endpoints (the default), no changes are
needed. PUBLIC_OPTIMIZATION_ENABLE_PREVIEW_PANEL="true" attaches the browser preview panel for
local and staging development runs. See .env.example for the full list.
react-web-sdk/
├── src/
│ ├── main.tsx # OptimizationRoot, preview panel attachment, createBrowserRouter
│ ├── App.tsx # Shared layout: SDK state, entry loading, nav, AnalyticsEventDisplay
│ ├── components/
│ │ ├── AnalyticsEventDisplay.tsx # Live event stream panel (persists across routes)
│ │ ├── ControlPanel.tsx # Consent, identify, reset, and conversion controls
│ │ └── RichTextRenderer.tsx # Rich text + merge tag rendering
│ ├── config/
│ │ └── locale.ts # Application Contentful locale
│ ├── contentful-generated.d.ts # Generated Contentful entry skeleton types
│ ├── pages/
│ │ ├── HomePage.tsx # Utility panel, live updates, entry sections
│ │ └── PageTwoPage.tsx # Navigation + conversion tracking demo
│ ├── sections/
│ │ ├── ContentEntry.tsx # Auto/manual tracked entry renderer
│ │ ├── LiveUpdatesExampleEntry.tsx # Live updates parity: default / locked / always-live
│ │ ├── NestedContentEntry.tsx # Nested personalization wrapper
│ │ └── NestedContentItem.tsx # Recursive nested entry via OptimizedEntry
│ ├── services/
│ │ └── contentfulClient.ts # Contentful CDA client
│ ├── types/
│ │ ├── contentful.ts # Entry type definitions
│ │ └── env.d.ts # import.meta.env typings
├── index.html
├── .env.example
├── package.json
├── rsbuild.config.ts
├── tsconfig.json
├── AGENTS.md
└── README.md
This implementation uses the official React Web SDK package directly. Keep API-level usage details in the @contentful/optimization-react-web package README.
Implementation-specific touchpoints:
src/main.tsx mounts OptimizationRoot, ReactRouterAutoPageTracker, route configuration, and
preview-panel attachment.src/sections/ContentEntry.tsx demonstrates automatic tracking props and manual view tracking.src/sections/LiveUpdatesExampleEntry.tsx compares default, locked, and always-live entry
resolution.src/components/ControlPanel.tsx demonstrates consent, identify, reset, and conversion actions.| File or area | Purpose |
|---|---|
src/main.tsx |
Configures OptimizationRoot and ReactRouterAutoPageTracker |
src/App.tsx |
Subscribes to provider state and renders route-level controls |
src/sections/ContentEntry.tsx |
Demonstrates OptimizedEntry tracking props and manual views |
src/sections/LiveUpdatesExampleEntry.tsx |
Demonstrates locked and live entry resolution |
src/components/RichTextRenderer.tsx |
Demonstrates merge tag rendering with useMergeTagResolver() |
src/components/AnalyticsEventDisplay.tsx |
Displays event stream output from sdk.states.eventStream |
Manual selectedOptimizations lock logic |
<OptimizedEntry liveUpdates={false}> |
What stays the same: contentfulClient.ts, locale config, type definitions, RichTextRenderer,
E2E test files, page/section component structure.
Key architectural difference: App.tsx acts as a persistent layout (contains
AnalyticsEventDisplay that stays mounted across route changes). Pages are route children that
receive state via useOutletContext.
@contentful/optimization-web