Skip to contentDevelopersPrezly website
Build a newsroom

Rich content

Understand the document tree, support rich blocks, and customize how a story renders.

Prezly content is a tree of elements and text leaves. Paragraphs and headings contain text; blocks such as images and attachments carry additional data. Render those structures rather than treating every node as a paragraph or concatenating their text.

See Story rendering for fetching and rendering a real story. This guide explains what the renderer receives and where you may need to customize it.

Read the document tree

This small v6 document includes title and subtitle roles, inline marks, and a callout:

{
  "type": "document",
  "version": "2.6",
  "children": [
    {
      "type": "heading-one",
      "role": "title",
      "children": [{ "text": "Acme launches its new collection" }]
    },
    {
      "type": "heading-two",
      "role": "subtitle",
      "children": [{ "text": "Designed for everyday use" }]
    },
    {
      "type": "paragraph",
      "children": [
        { "text": "Meet the " },
        { "text": "new collection", "bold": true },
        { "text": ". Available from today.", "highlighted": true }
      ]
    },
    {
      "type": "callout",
      "icon": "💡",
      "children": [{ "text": "Press images are available below." }]
    }
  ]
}

An element has a type, children, and properties specific to that type. A text leaf has text and may carry marks such as bold, italic, underlined, or highlighted. Empty text children can be part of a valid node; an image or attachment does not become empty just because its text is empty.

The content-format package provides the current types and node helpers. Node-specific properties matter: an image's uploaded-file object cannot be replaced with an arbitrary remote URL.

Support the blocks your authors use

ContentWhat your rendering needs to preserve
Headings, paragraphs, lists, quotes, and tablesStructure, text marks, alignment, and nested children.
Links and buttonsDestination, text, and intended link behavior. A button-block is a content block, not an API action.
Images, galleries, and attachmentsUploaded-file metadata, image effects, captions, and download links.
CalloutsTheir icon, text, and formatting; these require v6 support.
Video, embeds, and bookmarksTheir resolved embed information and a fallback when the provider cannot load.
Contacts, coverage, and story referencesThe referenced resource data and any application-specific context needed to display it.
HTMLAn explicit policy for handling raw markup.

The default React renderer does not replace your application's routing or every resource lookup. For example, coverage rendering accepts coverageEntries; linked story cards may need a custom component and the story's referenced_entities. Bea demonstrates these integrations in its ContentRenderer.

Do not silently discard unknown blocks when content or package versions advance. Choose a useful fallback, report the unsupported node type, and check whether the renderer needs an update.

Customize one node without replacing the renderer

Use Component rules to override a node type while retaining the default components for the rest. For example, if your page already displays the title and subtitle separately, this component renders the remaining document without duplicating them:

'use client';

import { Component, Elements, Renderer } from '@prezly/content-renderer-react-js';
import { DocumentNode, HeadingNode, HtmlNode } from '@prezly/story-content-format';

export function StoryBody({ document }: { document: DocumentNode }) {
  return (
    <Renderer nodes={document}>
      <Component match={HeadingNode.isTitleHeadingNode} component={Elements.Ignore} />
      <Component match={HeadingNode.isSubtitleHeadingNode} component={Elements.Ignore} />
      <Component match={HtmlNode.isHtmlNode} component={Elements.Ignore} />
    </Renderer>
  );
}

These overrides do not suppress the header image. If your page renders that separately too, identify the document's header node and handle it deliberately rather than removing every image.

When adding broader custom heading rules, put title/subtitle rules first: the more specific match should take precedence. The renderer package exposes the default Elements for reuse.

Decide how to handle HTML and external embeds

Structured text is rendered as text, but an html node contains raw markup. The default HTML component inserts that markup into the page. The examples above omit HTML nodes; an application that allows them should sanitize or otherwise constrain the markup before rendering it. Parsing JSON is not HTML sanitization.

External video and embed providers may load third-party scripts, frames, or cookies. Integrate them with your site's consent policy and provide a link or preview when an embed is unavailable. A restrictive content policy should give readers a useful alternative rather than an unexplained blank space.

Keep rendering separate from authoring

Rendering reads existing documents. Creating a document requires valid node properties and uploaded media references; a renderer does not upload files, resolve every embed URL, or save stories. See Images and files for the media representation. Continue with Creating and updating stories for structured documents, or Importing HTML for existing articles.

On this page