Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
72 changes: 69 additions & 3 deletions docs/6-rendering-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,20 @@ Your entire file should look like this:

```tsx
import { GraphClient } from '@optimizely/cms-sdk';
import { OptimizelyComponent } from '@optimizely/cms-sdk/react/server';
import {
OptimizelyComponent,
withAppContext,
} from '@optimizely/cms-sdk/react/server';
import React from 'react';

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

export default async function Page({ params }: Props) {
export async function Page({ params }: Props) {
const { slug } = await params;

const client = new GraphClient(process.env.OPTIMIZELY_GRAPH_SINGLE_KEY!, {
Expand All @@ -130,9 +134,71 @@ export default async function Page({ params }: Props) {

return <OptimizelyComponent content={content[0]} />;
}

export default withAppContext(Page);
```

Go again to http://localhost:3000/en. You should see your page
Go again to <http://localhost:3000/en>. You should see your page

### Understanding `withAppContext`

The `withAppContext` HOC wraps your page component to provide request-scoped context:

```tsx
export async function Page({ params }: Props) {
// ... component logic
}

export default withAppContext(Page);
```

**What it does:**

**Initializes context storage** - Sets up isolated, request-scoped storage for context data. This is required when using the context system in React Server Components.

**When do you need it:**

Since `getPreviewContent` automatically populates context with preview data, `withAppContext` is **optional for preview pages**. Use it when:

- You need context initialized before fetching content (e.g., for manual `setContextData` calls)
- You're using context for non-preview data
- You want to ensure context is available throughout the component tree

**Benefits:**

- **Request isolation** - Each request gets its own context storage (critical for server components)
- **No prop drilling** - Access context data anywhere in your component tree
- **Framework-agnostic** - Works with any React Server Components framework

### Accessing Context in Components

Any component can access the context data without props:

```tsx
import { getContext } from '@optimizely/cms-sdk/react/server';

export function MyComponent() {
const context = getContext();

// Access preview token, locale, etc.
const locale = context?.locale ?? 'en-US';
const isPreview = !!context?.preview_token;

return <div>Locale: {locale}</div>;
}
```

**How context is populated:**

Context data can be populated in two ways:

1. **Automatically by `getPreviewContent`** - This method automatically sets preview_token, locale, key, version, and ctx in the context
2. **Manually via `setContextData()`** - You can explicitly set context data when needed

This is particularly useful for:

- Displaying locale-specific formatting
- Getting content version and key for manual queries and rendering

## Next steps

Expand Down
77 changes: 75 additions & 2 deletions docs/7-live-preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ First, create a dedicated route for handling preview requests. In Next.js, creat

```tsx
import { GraphClient, type PreviewParams } from '@optimizely/cms-sdk';
import { OptimizelyComponent } from '@optimizely/cms-sdk/react/server';
import {
OptimizelyComponent,
withAppContext,
} 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) {
export async function Page({ searchParams }: Props) {
const client = new GraphClient(process.env.OPTIMIZELY_GRAPH_SINGLE_KEY!, {
graphUrl: process.env.OPTIMIZELY_GRAPH_GATEWAY,
});
Expand All @@ -44,10 +47,28 @@ export default async function Page({ searchParams }: Props) {
</>
);
}

export default withAppContext(Page);
```

Let's break down what's happening here:

### Wrapping with `withAppContext` (Optional)

```tsx
export default withAppContext(Page);
```

The `withAppContext` HOC initializes request-scoped context storage and is useful in both preview and regular mode:

**In Preview Mode:**

- Makes preview data (`preview_token`, `key`, `locale`, `version`) available throughout your component tree via the SDK
- Enables access to these values for custom querying or rendering scenarios
- Allows any nested component to retrieve preview context using `getContextData()`

See [Rendering (with React)](./6-rendering-react.md#understanding-withappcontext) for details.

### GraphClient Setup

```tsx
Expand All @@ -68,6 +89,8 @@ const response = await client.getPreviewContent(

The `getPreviewContent` method handles all the complexity of fetching the right content version based on the preview parameters sent from the CMS. These parameters are automatically included in the URL when an editor clicks "Preview" in the CMS.

**Automatic Context Population**: The `getPreviewContent` method automatically populates the global context with preview parameters (`preview_token`, `locale`, `key`, `version`, `ctx`). This means any component in your tree can access preview data via `getContextData()` without manual extraction. The `withAppContext` HOC is optional when using `getPreviewContent` - context is automatically set by the method itself.

### Rendering Preview Content

```tsx
Expand Down Expand Up @@ -222,3 +245,53 @@ export default function AboutUs({ content }: AboutUsProps) {

> [!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.

## Accessing Context Data in Components

When you wrap your preview route with `withAppContext`, it initializes request-scoped context for the incoming request. The `GraphClient.getPreviewContent()` call then extracts preview parameters from the URL and populates this context (via `setContext()`). Any component in your tree can access this data using `getContextData()`.

### Example: Custom Preview Banner

```tsx
import { getContextData } from '@optimizely/cms-sdk/react/server';

export function PreviewBanner() {
const preview_token = getContextData('preview_token');
const locale = getContextData('locale');

// Check if we're in preview mode
if (!preview_token) {
return null;
}

return (
<div className="preview-banner">
<p>Preview Mode - Locale: {context.locale ?? 'default'}</p>
</div>
);
}
```

### Available Context Properties

The context automatically includes:

- `preview_token` - Preview/edit mode authentication token
- `locale` - Content locale from `loc` parameter
- `key` - Content key identifier
- `version` - Content version from `ver` parameter

### Example: Locale-Aware Component

```tsx
import { getContextData } from '@optimizely/cms-sdk/react/server';

export function DateDisplay({ date }: { date: Date }) {
const locale = getContextData('locale') ?? 'en';

return <time>{date.toLocaleDateString(locale)}</time>;
}
```

> [!TIP]
> The `RichText` component automatically uses `preview_token` from context to append preview tokens to image URLs, so you don't need to manually handle this.
Loading
Loading