
Working with Contentful’s rich text in a Next.js project isn’t too complicated, but getting things to behave the way you want requires a few adjustments. By default, code blocks rendered from Contentful’s rich text field don’t let you specify the language for syntax highlighting. That becomes an issue if you want consistent styling and proper language-specific formatting.
This post outlines how to configure a custom renderer that works with @contentful/rich-text-react-renderer and react-syntax-highlighter to allow embedded code language declarations via a simple syntax.
Setup
This assumes you’re already fetching rich text data from Contentful and using the documentToReactComponents method to render it in your React components. You'll also need:
npm install react-syntax-highlighter
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
Solving the problem
Out of the box, Contentful gives you a code block, but no way to tell it what language the block is in. Everything gets lumped under a generic monospaced font, maybe highlighted as JavaScript if you’re lucky.
You can embed metadata into the code block content itself, using a simple !code="language" prefix. Here’s what that looks like in Contentful:
!code="bash"
npm install next react react-dom
Or, if you leave it out, it'll default to JavaScript:
You then configure your rich text renderer like this:
const options = {
renderMark: {
[MARKS.CODE]: (text: string) => {
let language = 'javascript';
let code = text;
const match = text.match(/^!code="(.+?)"\s*\n/);
if (match) {
language = match[1];
code = text.replace(match[0], '');
}
return (
<SyntaxHighlighter language={language} style={vscDarkPlus}>
{code}
</SyntaxHighlighter>
);
}
}
};
Then in your component:
!{documentToReactComponents(richTextDocument, options)}
Notes
The regex looks for a line at the top of the code block that starts with !code="language".
The matched line is stripped from the output.
If there’s no match, the fallback is JavaScript.
This works because Contentful stores code blocks as plain strings. It doesn’t sanitize out custom prefixes. So you can hijack that to pass in metadata.
Final Thoughts
There might be cleaner ways to annotate code blocks in other CMSs, but this works in Contentful without needing to restructure your content model or add new fields. It’s a bit hacky, but not brittle. If Contentful ever changes how code blocks are stored, this could break—but for now, it’s reliable.
Not trying to be clever here. It just solves the problem.