Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Live preview with React

Set up live preview in a Next.js application to let editors see and interact with real-time content changes in Optimizely CMS before publishing.

Live preview lets editors see content changes in real time before publishing. When an editor updates content in Optimizely CMS, the preview updates immediately without leaving the editor. Use this guide to set up live preview for a Next.js application using the Optimizely JavaScript SDK.

Prerequisites

Before you begin, ensure that you have:

  • A React-based application configured with the Optimizely JavaScript SDK (Next.js in this example)
  • Defined content types and registered React components
  • Access to your Optimizely CMS instance URL
  • Optimizely Graph credentials (single key and gateway URL)

1. Create a preview route

Create a dedicated route to handle preview requests from the CMS.

  1. In your Next.js project, create the following file:

    src/app/preview/page.tsx

  2. Add the following implementation:

import { GraphClient, type PreviewParams } from '@optimizely/cms-sdk';
import { OptimizelyComponent } from '@optimizely/cms-sdk/react/server';
import { PreviewComponent } from '@optimizely/cms-sdk/react/client';
import Script from 'next/script';

type Props = {
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};

export default async function Page({ searchParams }: Props) {
  const client = new GraphClient(process.env.OPTIMIZELY_GRAPH_SINGLE_KEY!, {
    graphUrl: process.env.OPTIMIZELY_GRAPH_GATEWAY,
  });

  const response = await client.getPreviewContent(
    (await searchParams) as PreviewParams
  );

  return (
    <>
      <Script
        src={`${process.env.OPTIMIZELY_CMS_URL}/util/javascript/communicationinjector.js`}
      ></Script>
      <PreviewComponent />
      <OptimizelyComponent opti={response} />
    </>
  );
}

Breaking down what is happening in the above code.

Initialize the Graph client

/*From Line 11 - 13 */
const client = new GraphClient(process.env.OPTIMIZELY_GRAPH_SINGLE_KEY!, {
  graphUrl: process.env.OPTIMIZELY_GRAPH_GATEWAY,
});

The GraphClient authenticates requests to Optimizely Graph. Store credentials in environment variables.

Fetch preview content

/*From Line 15 - 17 */
const response = await client.getPreviewContent(
  (await searchParams) as PreviewParams
);

getPreviewContent resolves the correct draft version based on preview parameters sent by the CMS.

Rendering Preview Content

/*From Line 19 - 28 */
return (
  <>
    <Script
      src={`${process.env.OPTIMIZELY_CMS_URL}/util/javascript/communicationinjector.js`}
    ></Script>
    <PreviewComponent />
    <OptimizelyComponent opti={response} />
  </>
);

These elements work together as follows:

  • Communication injector script – Enables two-way communication between the CMS editor and the preview window.
  • PreviewComponent – Listens for live updates and triggers re-renders when editors make changes.
  • OptimizelyComponent – Renders content using the registered React component for the content type.

2. Configure environment variables

Add the following environment variables to your .env.local file:

OPTIMIZELY_GRAPH_SINGLE_KEY=your_single_key_here
OPTIMIZELY_GRAPH_GATEWAY=https://cg.optimizely.com/content/v2
OPTIMIZELY_CMS_URL=https://your-cms-instance.optimizely.com

Replace the values with your actual:

  • OPTIMIZELY_GRAPH_SINGLE_KEY - Your Content Graph single key
  • OPTIMIZELY_GRAPH_GATEWAY - Your Content Graph gateway URL (default shown above)
  • OPTIMIZELY_CMS_URL - Your Optimizely CMS instance URL (without trailing slash)
🚧

Important

Never commit your .env.local file to version control. Add it to your .gitignore to keep your credentials secure.

3. Configure hostname and preview in CMS

Add hostname

  1. Open your application and go to the Hostnames tab.
  2. Click Add Hostname and enter your application URL:
    • local development: http://localhost:3000.
    • production: https://yourdomain.com.
  3. Select Use a secure connection (HTTPS) if applicable.
  4. Click Add.

Configure preview url

  1. Go to the Live Preview tab.
  2. Select Use Preview Tokens.
  3. Click Enabled under Preview URL format.
  4. A default format is added automatically - edit or add rows for specific content types.
  5. Update the preview URL to point to your preview route:
    • For local development: http://localhost:3000/preview.
    • For production: https://yourdomain.com/preview.
  6. Click Save
📘

Note

You can configure different preview URLs for multiple environments (local, staging, production) to test content across different deployment stages.

Use preview in other frameworks

While this guide focuses on Next.js, the SDK supports preview in other React-based frameworks. The core concepts remain the same:

  1. Create a preview route that accepts query parameters
  2. Use GraphClient.getPreviewContent() to fetch the content
  3. Load the communication injector script
  4. Render with PreviewComponent and OptimizelyComponent

Implementation details vary based on routing and rendering strategy.

Use preview utilities for on-page editing

To enable click-to-edit behavior in preview, add preview attributes to rendered elements using getPreviewUtils. This is how it would look like on Live Preview.

Live Preview with preview utils

Preview attributes

To implement on-page editing functionality in your code, you need to add preview attributes to your HTML or JSX elements using the getPreviewUtils utility. These attributes enable the click-to-edit behavior that allows content editors to navigate directly from the preview to the corresponding field in the CMS editor.

Understand preview attributes (pa)

The pa function (short for "preview attributes") enables visual editing in the CMS. When editors hover over elements with these attributes, they're highlighted to show they're editable. Clicking them jumps directly to the corresponding field in the CMS editor.

Complete example

Here's a complete component that demonstrates both pa for preview attributes and src for resolving content references:

import { contentType, damAssets, Infer } from '@optimizely/cms-sdk';
import { RichText } from '@optimizely/cms-sdk/react/richText';
import { getPreviewUtils } from '@optimizely/cms-sdk/react/server';

export const AboutUsContentType = contentType({
  key: 'AboutUs',
  baseType: '_component',
  properties: {
    heading: { type: 'string' },
    body: { type: 'richText' },
    image: {
      type: 'contentReference',
      allowedTypes: ['_image'],
    },
  },
});

type AboutUsProps = {
  opti: Infer<typeof AboutUsContentType>;
};

export default function AboutUs({ opti }: AboutUsProps) {
  const { pa, src } = getPreviewUtils(opti);
  const { getSrcset, getAlt } = damAssets(opti);

  return (
    <section className="about-us">
      {opti.image && (
        <div className="about-us-image">
          <img
            {...pa('image')}
            src={src(opti.image)}
            srcSet={getSrcset(opti.image)}
            sizes="(max-width: 768px) 100vw, 50vw"
            alt={getAlt(opti.image, 'About us image')}
          />
        </div>
      )}
      <h2 {...pa('heading')}>{opti.heading}</h2>
      <div {...pa('body')} className="about-us-content">
        <RichText content={opti.body?.json} />
      </div>
    </section>
  );
}

Key functions:

  • pa('propertyName') - Spreads preview attributes onto elements to enable on-page editing. Use this for all content properties (text, rich text, images, etc.). The property name must match your content type definition.
  • src(reference) - Resolves content reference URLs correctly in both preview and published states.
📘

Note

Apply pa() to all content properties to enable the full on-page editing experience. This allows editors to click elements in the preview and jump directly to the corresponding field in the CMS.