React Markdown component: the easy way to create rich text

Josip Miskovic
Josip Miskovic
Contributor

Feb 19, 2022

If you're building a text-heavy site in React—think a blog, documentation, or long product page—writing everything in hundreds of JSX tags is not going to be a good time. The React markdown component solves this problem by allowing you to write editorial content for your pages in Markdown, while also providing flexibility to apply custom styles and formatting. It lets you create rich text content without having to worry about all the formatting and code placement.

In this blog post, we will explore how react-markdown works and why you should start using it.

What is react-markdown?

React-markdown is a React component that safely renders markdown into HTML, ensuring compatibility across different browsers and platforms. It's built and maintained by UnifiedJS, the same folks behind RemarkJS. The basic idea is that you add the component into your render function, and can write Markdown in between your opening and closing tags. This allows you to easily generate dynamic content by rendering the body from inputs or other data sources.

React-markdown is used by Netlify, Stream Chat, Gatsby, and more. The npm package has over 900,000 weekly downloads. The react-markdown repository on GitHub has over 8200 stars and 715 forks. In other words, it’s decently popular.

React-markdown is available as an npm package.To install it, run the following command:

npm install react-markdown --save

It’s also available in Deno via Skypack.

React-markdown uses the CommonMark standard to render markdown content, making it compatible with modern browsers as well as legacy browsers.

Using react-markdown

React-markdown is very simple. The component takes markdown content as children:

1import React from 'react'
2import ReactDOM from 'react-dom'
3import ReactMarkdown from 'react-markdown'
4
5ReactDOM.render(
6  <ReactMarkdown>
7    [Retool](https://retool.com)
8    Man, imagine how *annoying* it would be to have to write **all** of this using HTML tags
9  </ReactMarkdown>,
10  document.querySelector('#content')
11)
12

Loading a markdown file into a React component

Markdown doesn't have to be mixed up with your React code. You can also load separate Markdown files (in your project or elsewhere) into the ReactMarkdown component using `fetch`:

1import { useEffect, useState } from "react";
2import ReactMarkdown from "react-markdown";
3
4const PageComponent = () => {
5  const [content, setContent] = useState("");
6
7  useEffect(() => {
8    fetch("Page.md")
9      .then((res) => res.text())
10      .then((text) => setContent(text));
11  }, []);
12
13  return (
14    <div className="post">
15      <ReactMarkdown children={content} />
16    </div>
17  );
18};
19

You could also import the Markdown file directly, but using fetch means the data gets pulled at runtime, and we can work with it asynchronously.

Here’s an example on CodeSandbox that uses an external .md file:

Custom Components

React-markdown also allows you to pass through custom components as markdown targets, giving you the freedom to apply custom styles or modify the rendering of specific elements in your markdown content.

For example, if you want react-markdown to use your custom link component instead of the default anchor element `<a>`, you can provide the component you want to render via the “components” prop:

1import React from 'react'
2import ReactDOM from 'react-dom'
3import ReactMarkdown from 'react-markdown'
4import CustomLink from './components/CustomLink'
5
6ReactDOM.render(
7  <ReactMarkdown
8    components={{
9      // Use `CustomLink` instead of anchor element `a`
10      a: CustomLink,
11    }}
12  >
13    [Retool](https://retool.com)
14  </ReactMarkdown>,
15  document.querySelector('#content')
16)
17import Link from 'next/link'
18export default function CustomLink({ as, href, ...otherProps }) {
19  return (
20      <Link as={as} href={href}>
21        <a {...otherProps} />
22      </Link>
23  )
24}
25

The components prop takes a dictionary of components, with keys as the default tags and values as whatever you want them to render as. In this example, we are telling react-markdown to use the Next.js Link component instead of the regular <a> tag. Using Link means our pages will pre-fetch any outbound links on the site, which can make performance snappier.

Security concerns with react-markdown

If you've worked with raw HTML in React, you've probably encountered dangerouslySetInnerHTML. It's a convenient property for injecting HTML, since it basically tells React to ignore the contents when it builds the virtual DOM, which can speed up performance; but it's also quite dangerous (shocker), as it makes your website vulnerable to XSS attacks.

React-markdown solves this problem because it uses a syntax tree to build the virtual DOM. A syntax tree is a data structure that represents the syntactic structure of a text. This approach allows for updating only the changes in the DOM instead of completely overwriting the entire tree. For those interested, WebAssembly uses a similar structure for their compiler.

The syntax tree approach renders actual components, not just unchecked HTML tags. So in that sense, react-markdown can be safer than using dangerouslySetInnerHTML.

Plugins

React-markdown is built by the same people who created Remark, one of the most popular tools to parse markdown and transform it into React components.

Remark shows up in a few packages:

You can pair react-markdown with remark plugins to enhance its functionality.

HTML in markdown

For the reverse problem (HTML in markdown), Rehype-raw lets you use HTML in your markdown content.

For example, if you want to create a heading wrapped in a custom div element, you can use the following syntax:

1import React from 'react'
2import ReactDom from 'react-dom'
3import ReactMarkdown from 'react-markdown'
4import rehypeRaw from 'rehype-raw'
5
6const input = `<div className=”custom">
7
8## JavaScript code
9
10</div>`
11
12ReactDom.render(
13  <ReactMarkdown rehypePlugins={[rehypeRaw]} children={input} />,
14  document.body
15)
16

If you find yourself using too much HTML in markdown, that's probably an anti-pattern. The markdown component is not meant to be used for creating complex layouts. Its purpose is to render markdown content into HTML so you can easily create rich text without having to worry about the code.

GitHub flavored markdown

React-markdown pairs with the remark-gfm plugin to support GitHub flavored markdown. That means you can use GitHub syntax to add autolink literals, footnotes, strike-through, tables, and task lists.

1 ~~I love jamstack~~                 
2https://www.thisbecomesalink.com
3* [x] yet another
4* [ ] to do
5

Conclusion

React-markdown is a simple way to create rich text in your React application without needing to work with endless JSX tags. Unlike other markdown components, it uses a syntax tree to build the virtual DOM. It's definitely a great choice for any React developer or content creator who wants to make rich-text a part of their application.

Give it a try and see for yourself how easy it is to use.

Josip Miskovic
Josip Miskovic
Contributor
Josip Miskovic is a full-stack software developer who specializes in ecommerce. Throughout his career, he used React to build ecommerce stores that serve over 10,000 customers daily.
Feb 19, 2022
Copied