In this page you will learn how to create a React component for your content type and how to render it.
Open the src/app/components/Article.tsx file and add the following
import { ContentProps } from '@optimizely/cms-sdk';
import { RichText } from '@episerver/cms-sdk/react/richText';
type Props = {
content: ContentProps<typeof ArticleContentType>;
};
export default function Article({ content }: Props) {
return (
<main>
<h1>{content.heading}</h1>
<RichText content={content.body?.json} />
</main>
);
}Note
For complete documentation on the RichText component including all props, advanced customization options, fallback handling, and TypeScript support, see the RichText Component Reference.
The entire file should look like this:
import { contentType, ContentProps } from '@optimizely/cms-sdk';
import { RichText } from '@optimizely/cms-sdk/react/richText';
export const ArticleContentType = contentType({
key: 'Article',
baseType: '_page',
properties: {
heading: {
type: 'string',
},
body: {
type: 'richText',
},
},
});
type Props = {
content: ContentProps<typeof ArticleContentType>;
};
export default function Article({ content }: Props) {
return (
<main>
<h1>{content.heading}</h1>
<RichText content={content.body?.json} />
</main>
);
}Edit the src/app/layout.tsx to register the Article component. Add the following snippet below initContentTypeRegistry():
initReactComponentRegistry({
resolver: {
Article,
},
});The entire layout.tsx should look like this:
import Article, { ArticleContentType } from '@/components/Article';
import { initContentTypeRegistry } from '@optimizely/cms-sdk';
import { initReactComponentRegistry } from '@optimizely/cms-sdk/react/server';
initContentTypeRegistry([ArticleContentType]);
initReactComponentRegistry({
resolver: {
Article,
},
});
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}Open src/app/[...slug]/page.tsx file and replace the last line inside the Page function:
- return <pre>{JSON.stringify(content[0], null, 2)}</pre>
+ return <OptimizelyComponent content={content[0]} />;Your entire file should look like this:
import { GraphClient } from '@optimizely/cms-sdk';
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 async function Page({ params }: Props) {
const { slug } = await params;
const client = new GraphClient(process.env.OPTIMIZELY_GRAPH_SINGLE_KEY!, {
graphUrl: process.env.OPTIMIZELY_GRAPH_GATEWAY,
});
const content = await client.getContentByPath(`/${slug.join('/')}/`);
return <OptimizelyComponent content={content[0]} />;
}
export default withAppContext(Page);Go again to http://localhost:3000/en. You should see your page
The withAppContext HOC wraps your page component to provide request-scoped context:
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. The context created is for the current routed content and lives only for the duration of the current request.
When do you need it:
Use withAppContext when you need request-scoped context storage:
- To manually set context data via
setContext()for the current request - To ensure context is available throughout the component tree
- When you need to pass content metadata or request-specific data to nested components
Benefits:
- Request isolation - Each request gets its own context storage for the routed content (critical for server components). Context data is scoped per request and automatically cleaned up when the request completes.
- No prop drilling - Access context data anywhere in your component tree
- Framework-agnostic - Works with any React Server Components framework
Any component can access the context data without props:
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:
Use setContext() to populate context data with information from your content:
setContext({
currentContent: content[0],
locale: content[0]?._metadata?.locale,
type: content[0]?.__typename,
key: content[0]?._metadata?.key,
});This is particularly useful for:
- Displaying locale-specific formatting
- Accessing content metadata in nested components without prop drilling
You have finished 🎉!
This is the end of the tutorial on how to create your first website using Optimizely CMS SaaS.
You can continue exploring these topics:
- Add Experiences - Learn how to create personalized content experiences for different audiences
- Add Live Preview - Enable real-time content editing and preview capabilities
- Add Display Settings - Configure how your content is displayed across different contexts