Rendering Markdown in React without react-markdown
Written by punkpeye on .
- Dangerously set inner HTML
- Rehype and Remark
- React hook to render markdown
- react-markdown vs a custom React hook
There appear to be literally a thousand ways to render markdown in React.
Up to now, I've been using react-markdown and I was happy with it.
However, I ran into an issue integrating KaTex and react-markdown did not make it easy to troubleshoot.
Dangerously set inner HTML
The main reason I chose to use react-markdown
initially was that it advertised it did not rely on dangerouslySetInnerHTML
.
It turns out that the same can be achieved by using rehype-react
, which is a plugin that transforms HTML into React components.
Rehype and Remark
To understand the Markdown-to-React conversion process, it's crucial to grasp the roles of rehype
and remark
.
remark
- Markdown processor that parses Markdown text into mdast (Markdown Abstract Syntax Tree) format.Key features:
Parses Markdown into a structured AST
Offers a plugin system for custom Markdown processing
Supports various Markdown flavors and extensions
rehype
- HTML processor that works with HAST (HTML Abstract Syntax Tree).Key features:
Processes HTML ASTs
Provides plugins for HTML modifications
Facilitates the conversion of HTML to other formats
These tools work together in a pipeline to convert Markdown into React elements:
remark
parses Markdown into mdastremark
plugins can modify the mdastThe mdast is converted to HAST (HTML AST)
rehype
plugins can modify the HASTFinally, the HAST is converted to React elements
React hook to render markdown
Upon becoming aware of the existence of rehype-react
, I started to implement a React hook that would allow me to render Markdown without using higher-level abstractions like react-markdown
.
This is what I came up with:
This code is heavily inspired byreact-remark
. The latter basically does the same thing, but depends on older versions of remark
and rehype
.
Here is how it works:
Parse Markdown using
remark-parse
Converts Markdown string to an Abstract Syntax Tree (AST)
Example: "# Hello" → heading node with text "Hello"
Apply remark plugins
Transforms Markdown AST (e.g., add syntax highlighting)
Transforms Markdown AST to HTML AST using
remark-rehype
Example: heading node →
<h1>
element
Apply rehype plugins
Modifies HTML AST (e.g., add attributes to links)
Convert to React elements using
rehype-react
Transforms HTML AST to React elements
Example:
<h1>
element →React.createElement('h1', null, 'Hello')
react-markdown
vs a custom React hook
I am not aware of any major differences between react-markdown
and the custom React hook I suggested.
Ultimately, even if the two implementations are equivalent, I prefer the custom React hook because it allows me a direct control over the conversion process.
The simplicity behind the custom React hook was a surprise to me and what prompted to write this post.
Written by punkpeye (@punkpeye)