React renderer for Contentful rich text field type that converts rich text AST documents into React components
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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>,
},
};