Mon May 30 2022

Example React component to render Contentful Rich Text

A quick code snippet showcasing how I render the rich text I get from a Contentful CMS

This isn't a how-to-do XYZ kind of blog post, but more of a post where I want to show how I created my own component to render Rich Text from a Contentful CMS using GraphQL.

So here is the code

1import Image from "next/image";
2import { documentToReactComponents } from "@contentful/rich-text-react-renderer";
3import {
4 BLOCKS,
5 MARKS,
6 INLINES,
7 Hyperlink,
8 AssetLinkBlock,
9 Paragraph,
10} from "@contentful/rich-text-types";
11import CodeRenderer from "./CodeRenderer";
12import Link from "next/link";
13import { RichTextRendererProps } from "../../singleBlogPostPage/interfaces";
14import {
15 RichTextContent,
16 SideBar,
17} from "../../singleBlogPostPage/SingleBlogPostPageStyles";
18import { NEW_ASSET } from "../interfaces";
19import PostPill from "../PostPill/PostPill";
20const RichTextRenderer = ({ content }: RichTextRendererProps) => {
21 // when rendering urls I first check whether they are external ones or not.
22 // if external I render an <a> tag. If internal I use the next/link
23 const website_url = "https://kishokanth.com/";
24 const { json, links } = content;
25 const Options = {
26 renderMark: {
27 [MARKS.BOLD]: (text: string) => <strong>{text}</strong>,
28 [MARKS.ITALIC]: (text: string) => <i>{text}</i>,
29 // CodeRenderer is the component which handles the rendering of code.
30 [MARKS.CODE]: (text: string) => <CodeRenderer code={text} />,
31 },
32 renderNode: {
33 [BLOCKS.PARAGRAPH]: (node: Paragraph, children: any) => {
34 return <p>{children}</p>;
35 },
36 [BLOCKS.QUOTE]: (node: Paragraph, children: any) => {
37 return <div className="blockQuote">{children}</div>;
38 },
39 // I chose to not render inline assets inside the rich text as
40 // I wanted to use the inline assets in a sidebar to display related posts
41 // you can see this in the return statement of this component
42 [NEW_ASSET.INLINE_ASSET]: (node: Paragraph, children: any) => {
43 return null;
44 },
45 [BLOCKS.EMBEDDED_ASSET]: (node: AssetLinkBlock) => {
46 const image = links?.assets.block.find(
47 (i) => i.sys.id === node.data.target.sys.id
48 );
49 if (image) {
50 return (
51 <Image
52 src={image?.url}
53 alt={image?.title}
54 layout="responsive"
55 width={image?.width}
56 height={image?.height}
57 />
58 );
59 }
60 return (
61 <p>
62 <i>
63 Ooops, we are having some trouble displaying an image that should
64 be here
65 </i>
66 </p>
67 );
68 },
69 [INLINES.HYPERLINK]: (node: Hyperlink) => {
70 const {
71 data: { uri },
72 content,
73 } = node;
74 return uri.includes(website_url) ? (
75 <Link href={uri}>{content[0].value}</Link>
76 ) : (
77 <a href={uri} rel="noopener noreferrer" target="_blank">
78 {content[0].value}
79 </a>
80 );
81 },
82 },
83 };
84 return (
85 <RichTextContent>
86 {documentToReactComponents(json, Options)}
87 <SideBar>
88 <h2>Related Posts</h2>
89 {links.entries.inline.map((card) => (
90 <PostPill {...card} key={card.slug} />
91 ))}
92 </SideBar>
93 </RichTextContent>
94 );
95};
96export default RichTextRenderer;

Of course, you can customise the above to fit more use cases. In fact, I'd love to see how you customised yours! Do share a link to a gist or your repo!

Comments