Contentful Personalization & Analytics
    Preparing search index...

    Contentful Logo

    Contentful Personalization & Analytics

    Optimization React Web SDK

    Guides · Reference · Contributing

    Warning

    The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.

    The Optimization React Web SDK provides React providers, hooks, router adapters, and entry-rendering primitives on top of the Optimization Web SDK. Use it when a React browser application must not manage the lower-level Web SDK instance, state subscriptions, entry resolution, and route tracking by hand.

    If you are integrating a React application, start with Getting Started, then use Integrating the Optimization React Web SDK in a React app for the step-by-step flow. This README keeps the package orientation and common setup options close at hand; generated reference documentation remains the source of truth for exported API signatures.

    Table of Contents

    Install using an NPM-compatible package manager, pnpm for example:

    pnpm install @contentful/optimization-react-web
    

    Mount OptimizationRoot once near the root of your React application:

    import { OptimizationRoot } from '@contentful/optimization-react-web'

    function App() {
    return (
    <OptimizationRoot clientId="your-client-id" environment="main">
    <YourApp />
    </OptimizationRoot>
    )
    }

    For a single-locale app that fetches Contentful entries, add default-only contentfulLocales:

    <OptimizationRoot
    clientId="your-client-id"
    environment="main"
    contentfulLocales={{
    default: 'en-US',
    }}
    >
    <YourApp />
    </OptimizationRoot>

    Use @contentful/optimization-react-web for React browser applications that need provider-based SDK initialization, hooks, router page tracking, optimized entry rendering, automatic interaction tracking, and live update semantics. Use the lower-level Web SDK directly for non-React integrations or custom framework adapters.

    OptimizationRoot accepts the Web SDK configuration props directly and adds React-specific props such as liveUpdates and onStatesReady.

    Prop Required? Default Description
    clientId Yes N/A Shared API key for Experience API and Insights API requests
    environment No 'main' Contentful environment identifier
    api No Web SDK defaults Experience API and Insights API endpoint and request options
    app No undefined Application metadata attached to outgoing event context
    contentfulLocales No undefined Contentful locale codes used for SDK-assisted CDA locale resolution
    locale No undefined unless contentfulLocales is set Initial app/content locale candidate; changing this prop updates the owned SDK locale
    defaults No undefined Initial state, commonly including consent, persistence consent, or profile values
    allowedEventTypes No ['identify', 'page'] Event types allowed before consent is explicitly set
    trackEntryInteraction No { views: true, clicks: false, hovers: false } Automatic entry interaction tracking for OptimizedEntry elements
    cookie No { domain: undefined, expires: 365 } Anonymous ID cookie settings inherited from the Web SDK
    liveUpdates No false Whether OptimizedEntry components react continuously to SDK state
    onStatesReady No undefined Provider-managed app-level state subscription hook
    queuePolicy No SDK defaults Flush retry behavior and offline queue bounds
    logLevel No 'error' Minimum log level for the default console sink
    onEventBlocked No undefined Callback invoked when consent or guard logic blocks an event

    Use OptimizationProvider directly only when an application or framework adapter must own a pre-built SDK instance:

    <OptimizationProvider sdk={optimization}>
    <YourApp />
    </OptimizationProvider>

    Injected SDK instances render children immediately unless onStatesReady is provided. When onStatesReady is provided, the provider waits until those state subscribers are attached before children mount, and still leaves SDK teardown to the owner that created the instance.

    For every Web SDK option that passes through this package, use the Web SDK README and generated reference documentation.

    Use contentfulLocales.default for single-locale apps, and add contentfulLocales.supported for localized apps that need browser locale matching.

    For apps that match browser locale input to multiple Contentful locales, keep default as the fallback and list the supported Contentful locale codes:

    <OptimizationRoot
    clientId="your-client-id"
    contentfulLocales={{
    default: 'en-US',
    supported: ['en-US', 'de-DE', 'fr-FR'],
    }}
    >
    <YourApp />
    </OptimizationRoot>

    Copy contentfulLocales.default and optional contentfulLocales.supported from Contentful locale settings or the CMA locale list. The resolved optimization.locale, when present, is the configured Contentful locale code to use for CDA fetches and default Experience API localization. The simplest path is useOptimization().withOptimizationLocale(contentfulClient) when fetching entries; data layers that need direct control can pass optimization.locale explicitly instead. See Locale handling in the Optimization SDK Suite for the full locale model.

    For provider-owned SDK instances, changing the locale prop calls sdk.setLocale() after initialization while the rest of the SDK config remains initialization-scoped. Locale updates do not fetch content or refresh profile state; trigger your app's normal page(), identify(), route loader, or CDA fetch flow when localized data needs to change.

    Consent policy remains application-owned. For default-on application policies that do not render an end-user consent UI, seed accepted consent on OptimizationRoot:

    <OptimizationRoot clientId="your-client-id" defaults={{ consent: true }}>
    <YourApp />
    </OptimizationRoot>

    When application policy depends on user choice, leave defaults.consent unset and call consent() from the relevant control:

    import { useOptimization } from '@contentful/optimization-react-web'

    function ConsentButton() {
    const sdk = useOptimization()
    return <button onClick={() => sdk.consent(true)}>Accept</button>
    }

    Boolean consent calls control both event emission and durable profile-continuity persistence by default. Use sdk.consent({ events: true, persistence: false }) when events are allowed but continuity should stay session-only. For cross-SDK consent guidance, see Consent management in the Optimization SDK Suite.

    OptimizationRoot owns the Web SDK lifecycle. Provider-owned initialization runs after React commit, outside render, and renders no children while the SDK is pending. In normal browser rendering this uses a layout-effect path so ready children can mount before the first visible paint.

    Use useOptimization() when a component needs direct access to the instance for methods such as identify(), reset(), or manual tracking. Use useEntryResolver() when a component needs manual entry resolution without the OptimizedEntry wrapper:

    import { useEntryResolver } from '@contentful/optimization-react-web'

    function HeroEntry({ baselineEntry }) {
    const { resolveEntry } = useEntryResolver()
    const resolvedEntry = resolveEntry(baselineEntry)

    return <HeroCard entry={resolvedEntry} />
    }

    Fetch Contentful entries in the app layer with one CDA locale. For localized apps, configure contentfulLocales, then use useOptimization().withOptimizationLocale(contentfulClient) or pass optimization.locale explicitly before passing entries to OptimizedEntry, useEntryResolver(), or useOptimizedEntry(). Without contentfulLocales or an explicit top-level locale, the wrapper omits the CDA locale and lets Contentful use the space default. Do not pass all-locale CDA responses from withAllLocales or locale=*; these APIs expect direct single-locale field values. See Entry personalization and variant resolution for the entry contract and Locale handling in the Optimization SDK Suite for runtime locale behavior.

    Use useMergeTagResolver() when a component needs to resolve embedded merge tag entries:

    import { useMergeTagResolver } from '@contentful/optimization-react-web'

    function MergeTagText({ mergeTagEntry }) {
    const { getMergeTagValue } = useMergeTagResolver()

    return <span>{getMergeTagValue(mergeTagEntry) ?? ''}</span>
    }

    If a merge tag references localized profile fields such as location.city or location.country, its resolved value follows the localized profile values returned by the Experience API.

    Use onStatesReady when application code needs to subscribe to SDK state as part of provider initialization. This avoids coordinating with window.contentfulOptimization, which may not exist yet when application code runs or may have already emitted data by the time a later effect subscribes.

    <OptimizationRoot
    clientId="your-client-id"
    onStatesReady={(states) => {
    const subscriptions = [
    states.eventStream.subscribe((event) => {
    if (event) devToolsPanel.logEvent(event)
    }),
    states.blockedEventStream.subscribe((blocked) => {
    if (blocked) devToolsPanel.logBlockedEvent(blocked)
    }),
    ]

    return () => {
    subscriptions.forEach((subscription) => subscription.unsubscribe())
    }
    }}
    >
    <YourApp />
    </OptimizationRoot>

    The callback receives only sdk.states. It runs as soon as the provider has initialized the state surface and before children mount, so subscriptions can observe events emitted by child effects such as router page tracking. For component-local UI state, keep using hooks and React effects under the provider.

    OptimizedEntry resolves a baseline Contentful entry and renders either the selected variant or the baseline entry:

    import { OptimizedEntry } from '@contentful/optimization-react-web'

    function HeroEntry({ baselineEntry }) {
    return (
    <OptimizedEntry baselineEntry={baselineEntry}>
    {(resolvedEntry) => <HeroCard entry={resolvedEntry} />}
    </OptimizedEntry>
    )
    }

    Use loadingFallback, direct children, wrapper props, and nested composition patterns when needed. The React Web guide covers those variants in context.

    Entry interaction tracking

    OptimizedEntry emits the Web SDK's data-ctfl-* tracking attributes for resolved entries. Enable automatic tracking in the root config when views, clicks, or hovers must be detected by the Web SDK:

    <OptimizationRoot
    clientId="your-client-id"
    trackEntryInteraction={{ views: true, clicks: true, hovers: false }}
    >
    <YourApp />
    </OptimizationRoot>

    Use sdk.tracking.enableElement(...) from useOptimization() for manual element overrides.

    Router adapters emit page() events for supported client-side routers:

    Router Import path Mounting rule
    React Router @contentful/optimization-react-web/router/react-router Mount under a React Router data router and inside OptimizationRoot
    Next.js Pages @contentful/optimization-react-web/router/next-pages Mount once in pages/_app.tsx inside OptimizationRoot
    Next.js App Router @contentful/optimization-react-web/router/next-app Mount in a client provider used by app/layout.tsx
    TanStack Router @contentful/optimization-react-web/router/tanstack-router Mount under the TanStack router tree and inside OptimizationRoot

    All adapters support static and dynamic page payload enrichment. See the React Web integration guide for router-specific examples.

    liveUpdates defaults to false, so optimized entries lock to the first resolved value. Set liveUpdates globally or per OptimizedEntry when entries must react to profile, flag, or preview changes:

    <OptimizationRoot clientId="your-client-id" liveUpdates={true}>
    <OptimizedEntry baselineEntry={entry} liveUpdates={false}>
    {(resolvedEntry) => <Card entry={resolvedEntry} />}
    </OptimizedEntry>
    </OptimizationRoot>

    The browser preview panel is provided by @contentful/optimization-web-preview-panel. When the panel is open, live updates are forced on for all OptimizedEntry components so authors can inspect variant changes immediately.

    The package-local development harness runs from packages/web/frameworks/react-web-sdk/dev/. Launch it from the repo root:

    pnpm --filter @contentful/optimization-react-web dev:launch
    

    Use the harness for package development. Use the reference implementation for end-to-end integration behavior.