Can I use this?

This feature is available since Webiny v5.39.0.

What you’ll learn
  • how to add a custom rich text content renderer


Headless CMS contains a rich text content field, which allows users to create content using a rich text editor. By default, Webiny uses the Lexical Editorexternal link, but developers are able to implement whatever editor they want for their projects. This means that the data stored into the database can, and most likely will, be very different between various rich text editors.

The Headless CMS GraphQL schema exposes a format parameter on rich text fields, to allow content consumers to request a specific format to be returned to them from the API. Out of the box, Webiny provides an HTML renderer for its default Lexical editor implementation. However, your project might use a different rich text editor, or you might want to support a different output format, for example, markdown.

Headless CMS HTML OutputHeadless CMS HTML Output
(click to enlarge)

To inspect the implementation of our built-in Lexical-to-HTML renderer, see the source code on Githubexternal link.

Plugin Registration

Code examples below demonstrate the creation of plugin instances which need to be registered in the apps/api/graphql/src/index.ts. Simply import your plugins, and append them to the bottom of the plugins array in the handler definition.

Create a Custom Renderer

Create a plugin file

The code example is pointing to a file which doesn’t yet exist in your project. Create this file, or place the code into a different location of choice.

To create a custom renderer, create a plugin similar to the one shown in the code example below.


This creates a renderer that is executed when the content consumer specifies html as the output format of the rich text field in the GraphQL query. myHTMLRenderer is a fictional function to demonstrate the use of the plugin. The implementation of the actual HTML (or any other format) rendering will depend on the rich text editor that you use in your project, and the format you’re rendering to.

You’ve probably noticed the use of the dynamic import, and the webpackChunkName inline comment. We highly recommend you use code splitting to only load your renderer when it’s actually being used. This will reduce the size of the main code bundle that the Lambda function will have to process for common request processing. The webpackChunkName is recommended for easier debugging, as this name will be used as the chunk name, when the code bundle is built.

Intercept Existing Renderer

Sometimes, you might want to let the default renderer do its job, but fine tune the output before returning it to the consumer. This way, you can build multiple plugins that form a chain of transformations.

When there are multiple plugins registered to handle the same format, a chain is created, where the last plugin registered will be the first one executed. You will be able to call the next plugin in the chain using the next callback. This way you can completely intercept all previous plugins, and either skip them all together, or execute them at will.