React renderer for Contentful's rich text field type that converts rich text AST (Abstract Syntax Tree) documents into React components. It provides a flexible rendering system with customizable renderers for different node types (blocks, inlines) and text marks, enabling developers to override default HTML output with custom React components.
npm install @contentful/rich-text-react-rendererimport { documentToReactComponents } from "@contentful/rich-text-react-renderer";
import type { Options, RenderNode, RenderMark, RenderText, CommonNode } from "@contentful/rich-text-react-renderer";
// Import node and mark type constants from rich-text-types
import { BLOCKS, INLINES, MARKS } from "@contentful/rich-text-types";For CommonJS:
const { documentToReactComponents } = require("@contentful/rich-text-react-renderer");
const { BLOCKS, INLINES, MARKS } = require("@contentful/rich-text-types");import { documentToReactComponents } from "@contentful/rich-text-react-renderer";
const document = {
nodeType: "document",
data: {},
content: [
{
nodeType: "paragraph",
data: {},
content: [
{
nodeType: "text",
value: "Hello world!",
marks: [],
data: {},
},
],
},
],
};
// Basic rendering with default renderers
const reactComponents = documentToReactComponents(document);
// Returns: <p>Hello world!</p>Main function to serialize a Contentful Rich Text document to React tree with optional custom renderers.
/**
* Serialize a Contentful Rich Text document to React tree
* @param richTextDocument - The Contentful rich text document to render
* @param options - Optional configuration for custom rendering
* @returns React components tree or null if no document provided
*/
function documentToReactComponents(
richTextDocument: Document,
options?: Options
): ReactNode;Usage with Custom Renderers:
import { BLOCKS, MARKS } from "@contentful/rich-text-types";
import { documentToReactComponents } from "@contentful/rich-text-react-renderer";
const options = {
renderMark: {
[MARKS.BOLD]: (text) => <strong className="font-bold">{text}</strong>,
},
renderNode: {
[BLOCKS.PARAGRAPH]: (node, children) => (
<p className="my-paragraph">{children}</p>
),
[BLOCKS.EMBEDDED_ENTRY]: (node) => {
const { title, description } = node.data.target.fields;
return (
<div className="embedded-entry">
<h3>{title}</h3>
<p>{description}</p>
</div>
);
},
},
renderText: (text) => text.replace("!", "?"),
preserveWhitespace: true,
};
const reactComponents = documentToReactComponents(document, options);Custom text rendering with whitespace preservation support.
/**
* Custom text renderer function interface
* @param text - The text content to render
* @returns React node representation of the text
*/
interface RenderText {
(text: string): ReactNode;
}Usage for Line Break Handling:
const options = {
renderText: (text) => {
return text.split('\n').reduce((children, textSegment, index) => {
return [...children, index > 0 && <br key={index} />, textSegment];
}, []);
},
};Preserves multiple spaces and line breaks in rich text content.
const options = {
preserveWhitespace: true,
};
const document = {
nodeType: "document",
content: [
{
nodeType: "paragraph",
content: [
{
nodeType: "text",
value: "Hello world!",
marks: [],
},
],
},
],
};
documentToReactComponents(document, options);
// Returns: <p>Hello world!</p>Main configuration object for customizing rendering behavior.
interface Options {
/** Node renderers for blocks and inlines */
renderNode?: RenderNode;
/** Mark renderers for text formatting */
renderMark?: RenderMark;
/** Text renderer for processing text nodes */
renderText?: RenderText;
/** Keep line breaks and multiple spaces */
preserveWhitespace?: boolean;
}Custom renderer interfaces for different content types.
/**
* Function interface for custom node renderers
* @param node - The block or inline node to render
* @param children - Rendered children React nodes
* @returns React node representation
*/
interface NodeRenderer {
(node: Block | Inline, children: ReactNode): ReactNode;
}
/**
* Object mapping node types to custom renderer functions
*/
interface RenderNode {
[k: string]: NodeRenderer;
}
/**
* Object mapping mark types to custom mark renderer functions
*/
interface RenderMark {
[k: string]: (text: ReactNode) => ReactNode;
}Union type for all supported rich text node types.
/**
* Union type for all supported node types in rich text documents
*/
type CommonNode = Text | Block | Inline;The renderNode keys support the following BLOCKS constants:
BLOCKS.DOCUMENT - Root document containerBLOCKS.PARAGRAPH - Paragraph text blockBLOCKS.HEADING_1 through BLOCKS.HEADING_6 - Heading levels 1-6BLOCKS.UL_LIST - Unordered listBLOCKS.OL_LIST - Ordered listBLOCKS.LIST_ITEM - List itemBLOCKS.QUOTE - BlockquoteBLOCKS.HR - Horizontal ruleBLOCKS.TABLE - Table containerBLOCKS.TABLE_ROW - Table rowBLOCKS.TABLE_HEADER_CELL - Table header cellBLOCKS.TABLE_CELL - Table data cellBLOCKS.EMBEDDED_ENTRY - Embedded entry (block-level)BLOCKS.EMBEDDED_ASSET - Embedded asset (block-level)BLOCKS.EMBEDDED_RESOURCE - Embedded resource (block-level)The renderNode keys support the following INLINES constants:
INLINES.EMBEDDED_ENTRY - Embedded entry (inline)INLINES.EMBEDDED_RESOURCE - Embedded resource (inline)INLINES.HYPERLINK - Standard hyperlinkINLINES.ENTRY_HYPERLINK - Link to Contentful entryINLINES.ASSET_HYPERLINK - Link to Contentful assetINLINES.RESOURCE_HYPERLINK - Link to Contentful resourceThe renderMark keys support the following MARKS constants:
MARKS.BOLD - Bold text formattingMARKS.ITALIC - Italic text formattingMARKS.UNDERLINE - Underline text formattingMARKS.CODE - Inline code formattingMARKS.SUPERSCRIPT - Superscript text formattingMARKS.SUBSCRIPT - Subscript text formattingMARKS.STRIKETHROUGH - Strikethrough text formattingThe package provides sensible default HTML renderers for all supported node and mark types:
When using custom renderers, avoid passing index-like keys (e.g., 1 or "1") as they may clash with default key generation. Append non-numeric characters to custom keys:
const options = {
renderMark: {
[MARKS.BOLD]: (text) => <b key={`${text}-key`}>{text}</b>,
},
};