Story rendering
Retrieve a story in format v6 and render its structured content in a React application.
A custom newsroom needs more than a successful API response: it needs to turn a story's content document into a page. Fetch the story on your server, request a known format, and pass the resulting content to a compatible renderer.
For a complete Next.js newsroom with routing, styling, and integrations already in place, start with the Bea theme. The smaller examples below explain the underlying rendering path.
1. Retrieve the full story on the server
Use the token you loaded in Your first API request. Replace YOUR_STORY_UUID, save this Node.js 22+ example as inspect-story.mjs, and run node inspect-story.mjs:
const token = process.env.PREZLY_API_TOKEN;
if (!token) throw new Error('Set PREZLY_API_TOKEN before running this script.');
const response = await fetch('https://api.prezly.com/v2/stories/YOUR_STORY_UUID', {
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json; content-format=v6',
},
signal: AbortSignal.timeout(15_000),
});
if (!response.ok) throw new Error(`Prezly returned HTTP ${response.status}.`);
const { story } = await response.json();
if (story.format_version !== 6) throw new Error('Expected story format v6.');
const document = JSON.parse(story.content);
if (document?.type !== 'document' || !Array.isArray(document.children)) {
throw new Error('Expected a structured content document.');
}
console.log(document.type, document.version);GET /stories/{story_id} returns the full story under story. A list result may not contain content unless you request it. The SDK guide shows the equivalent typed client call.
Keep the token on the server. A browser component that renders a story does not need the credential. For a public page, check the story belongs to the intended newsroom and is both published and public before exposing it. A successful authenticated read can also return private content.
2. Install the renderer
In your React application:
pnpm add @prezly/content-renderer-react-js @prezly/story-content-formatThe renderer requires React and React DOM; the versions checked for this guide support React 18 and 19. Add its stylesheet once in your application's global stylesheet entry or root layout:
import '@prezly/content-renderer-react-js/styles.css';The stylesheet supplies the default block presentation. It does not supply a complete newsroom theme.
3. Render the document
Save this component as StoryContent.tsx. In a Next.js App Router application, the client boundary lets the renderer use its interactive blocks; fetch the story in server code and pass the story data to this component. Other React applications can omit the use client directive.
'use client';
import { Component, Elements, Renderer } from '@prezly/content-renderer-react-js';
import { DocumentNode, HtmlNode } from '@prezly/story-content-format';
export function StoryContent({
story,
}: {
story: { format_version: number; content: string };
}) {
if (story.format_version !== 6) throw new Error('Expected story format v6.');
const document = JSON.parse(story.content);
if (!DocumentNode.isDocumentNode(document)) {
throw new Error('Expected a structured content document.');
}
return (
<Renderer nodes={document}>
<Component match={HtmlNode.isHtmlNode} component={Elements.Ignore} />
</Renderer>
);
}This renders the document's own title, subtitle, and body. The HTML-node override deliberately omits raw HTML blocks; replace it with a renderer appropriate to your application's HTML policy if you need those blocks. The document check identifies the root shape, not a complete validation or sanitization of every nested value.
The renderer's default components cover common rich text and media. Review rich content for embedded resources, custom components, and content policies, and images and files for media URLs and image effects.
4. Integrate it into your page
Pass the retrieved story as <StoryContent story={story} />. Use story.title for the browser title, cards, or metadata, but do not add another visible article heading unless you also suppress the title node. Apply the same rule to the header image.
Decide which content your public routes can serve before adding caching. Keep private previews separate from public pages, and include the newsroom, language, and requested format in your cache design. Do not cache a credential-bearing response as a public page just because its fetch succeeded.
Before shipping, check a story containing formatted headings, links, an image, an attachment, a callout, and an embed on desktop and mobile. Test failure and unknown-block behavior as well as a simple paragraph. Content formats explains compatibility and conversion when your renderer and the API disagree.