What is @contentful/rich-text-react-renderer?
@contentful/rich-text-react-renderer is an npm package that allows developers to render Contentful's rich text content in React applications. It provides a set of tools to convert Contentful's rich text JSON structure into React components, making it easier to display rich text content in a React-based frontend.
What are @contentful/rich-text-react-renderer's main functionalities?
Rendering Rich Text
This feature allows you to render Contentful's rich text JSON structure as React components. The code sample demonstrates how to convert a simple rich text document into React elements.
const { documentToReactComponents } = require('@contentful/rich-text-react-renderer');
const richTextDocument = {
nodeType: 'document',
content: [
{
nodeType: 'paragraph',
content: [
{
nodeType: 'text',
value: 'Hello, world!',
marks: [],
data: {}
}
],
data: {}
}
],
data: {}
};
const RichTextComponent = () => (
<div>
{documentToReactComponents(richTextDocument)}
</div>
);
Customizing Renderers
This feature allows you to customize how different node types are rendered. The code sample shows how to apply a custom class to paragraph elements in the rich text content.
const { documentToReactComponents } = require('@contentful/rich-text-react-renderer');
const options = {
renderNode: {
'paragraph': (node, children) => <p className="custom-paragraph">{children}</p>
}
};
const richTextDocument = {
nodeType: 'document',
content: [
{
nodeType: 'paragraph',
content: [
{
nodeType: 'text',
value: 'Hello, world!',
marks: [],
data: {}
}
],
data: {}
}
],
data: {}
};
const RichTextComponent = () => (
<div>
{documentToReactComponents(richTextDocument, options)}
</div>
);
Rendering Embedded Assets
This feature allows you to render embedded assets such as images within the rich text content. The code sample demonstrates how to render an image from the rich text document.
const { documentToReactComponents } = require('@contentful/rich-text-react-renderer');
const options = {
renderNode: {
'embedded-asset-block': (node) => (
<img src={node.data.target.fields.file.url} alt={node.data.target.fields.title} />
)
}
};
const richTextDocument = {
nodeType: 'document',
content: [
{
nodeType: 'embedded-asset-block',
data: {
target: {
fields: {
file: { url: 'https://example.com/image.jpg' },
title: 'Example Image'
}
}
}
}
],
data: {}
};
const RichTextComponent = () => (
<div>
{documentToReactComponents(richTextDocument, options)}
</div>
);
Other packages similar to @contentful/rich-text-react-renderer
react-markdown
react-markdown is a React component that converts Markdown text to React components. It is similar to @contentful/rich-text-react-renderer in that it transforms a text-based format into React elements, but it specifically handles Markdown rather than Contentful's rich text JSON structure.
draft-js
draft-js is a framework for building rich text editors in React. It provides a more comprehensive set of tools for creating and managing rich text content, including editing capabilities, whereas @contentful/rich-text-react-renderer focuses on rendering Contentful's rich text JSON.
slate
slate is another framework for building rich text editors in React. It offers a high degree of customization and control over the editor's behavior and output, making it more suitable for complex editing needs compared to @contentful/rich-text-react-renderer, which is primarily for rendering purposes.
rich-text-react-renderer
React renderer for the Contentful rich text field type.
Installation
Using npm:
npm install @contentful/rich-text-react-renderer
Using yarn:
yarn add @contentful/rich-text-react-renderer
Usage
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: {},
},
],
},
],
};
documentToReactComponents(document);
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
const document = {
nodeType: 'document',
content: [
{
nodeType: 'paragraph',
content: [
{
nodeType: 'text',
value: 'Hello',
marks: [{ type: 'bold' }],
},
{
nodeType: 'text',
value: ' world!',
marks: [{ type: 'italic' }],
},
],
},
],
};
documentToReactComponents(document);
You can also pass custom renderers for both marks and nodes as an optional parameter like so:
import { BLOCKS, MARKS } from '@contentful/rich-text-types';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
const document = {
nodeType: 'document',
content: [
{
nodeType: 'paragraph',
content: [
{
nodeType: 'text',
value: 'Hello',
marks: [{ type: 'bold' }],
},
{
nodeType: 'text',
value: ' world!',
marks: [{ type: 'italic' }],
},
],
},
],
};
const Bold = ({ children }) => <p className="bold">{children}</p>;
const Text = ({ children }) => <p className="align-center">{children}</p>;
const options = {
renderMark: {
[MARKS.BOLD]: (text) => <Bold>{text}</Bold>,
},
renderNode: {
[BLOCKS.PARAGRAPH]: (node, children) => <Text>{children}</Text>,
},
renderText: (text) => text.replace('!', '?'),
};
documentToReactComponents(document, options);
Last, but not least, you can pass a custom rendering component for an embedded entry:
import { BLOCKS } from '@contentful/rich-text-types';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
const document = {
nodeType: 'document',
content: [
{
nodeType: 'embedded-entry-block',
data: {
target: (...)Link<'Entry'>(...);
},
},
]
};
const CustomComponent = ({ title, description }) => (
<div>
<h2>{title}</h2>
<p>{description}</p>
</div>
);
const options = {
renderNode: {
[BLOCKS.EMBEDDED_ENTRY]: (node) => {
const { title, description } = node.data.target.fields;
return <CustomComponent title={title} description={description} />
}
}
};
documentToReactComponents(document, options);
The renderNode
keys should be one of the following BLOCKS
and INLINES
properties as defined in @contentful/rich-text-types
:
-
BLOCKS
DOCUMENT
PARAGRAPH
HEADING_1
HEADING_2
HEADING_3
HEADING_4
HEADING_5
HEADING_6
UL_LIST
OL_LIST
LIST_ITEM
QUOTE
HR
EMBEDDED_ENTRY
EMBEDDED_ASSET
EMBEDDED_RESOURCE
-
INLINES
EMBEDDED_ENTRY
(this is different from the BLOCKS.EMBEDDED_ENTRY
)EMBEDDED_RESOURCE
HYPERLINK
ENTRY_HYPERLINK
ASSET_HYPERLINK
RESOURCE_HYPERLINK
The renderMark
keys should be one of the following MARKS
properties as defined in @contentful/rich-text-types
:
The renderText
callback is a function that has a single string argument and returns a React node. Each text node is evaluated individually by this callback. A possible use case for this is to replace instances of \n
produced by Shift + Enter
with <br/>
React elements. This could be accomplished in the following way:
const options = {
renderText: (text) => {
return text.split('\n').reduce((children, textSegment, index) => {
return [...children, index > 0 && <br key={index} />, textSegment];
}, []);
},
};
Note on adding a key
prop in custom renderers:
It is possible to pass a key
prop in the components returned by custom renderers. A good use case for this is in embeded entries using the node's target.sys.id
. It is important not to pass anything that is index-like (e.g. 1 or "1") as it may clash with the default renderers which automatically inject a key
prop using their index in the Contentful rich text AST.
To work around this limitation, just append any non-numeric character to your custom key.
const options = {
renderMark: {
[MARKS.BOLD]: (text) => {
return <b key={`${text}-key`}>{text}</b>;
},
},
};
Preserving Whitespace
The options
object can include a preserveWhitespace
boolean flag. When set to true
, this flag ensures that multiple spaces in the rich text content are preserved by replacing them with
, and line breaks are maintained with <br />
tags. This is useful for content that relies on specific formatting using spaces and line breaks.
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
const document = {
nodeType: 'document',
content: [
{
nodeType: 'paragraph',
content: [
{
nodeType: 'text',
value: 'Hello world!',
marks: [],
},
],
},
],
};
const options = {
preserveWhitespace: true,
};
documentToReactComponents(document, options);
In this example, the multiple spaces between "Hello" and "world!" are preserved in the rendered output.