Skip to main content

React Markdown component: the easy way to create rich text

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 (opens in a new tab) 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 (opens in a new tab) is a React component that safely renders markdown into HTML, ensuring compatibility across different browsers and platforms. It's built and maintained by UnifiedJS (opens in a new tab), the same folks behind RemarkJS (opens in a new tab). 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 (opens in a new tab), Stream Chat (opens in a new tab), Gatsby (opens in a new tab), 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 (opens in a new tab).

React-markdown uses the CommonMark (opens in a new tab) 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:

PlainText

import React from 'react'
import ReactDOM from 'react-dom'
import ReactMarkdown from 'react-markdown'

ReactDOM.render(
  <ReactMarkdown>
    [Retool](https://retool.com)
    Man, imagine how *annoying* it would be to have to write **all** of this using HTML tags
  </ReactMarkdown>,
  document.querySelector('#content')
)

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`:

PlainText

import { useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";

const PageComponent = () => {
  const [content, setContent] = useState("");

  useEffect(() => {
    fetch("Page.md")
      .then((res) => res.text())
      .then((text) => setContent(text));
  }, []);

  return (
    <div className="post">
      <ReactMarkdown children={content} />
    </div>
  );
};

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 (opens in a new tab) 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:

PlainText

import React from 'react'
import ReactDOM from 'react-dom'
import ReactMarkdown from 'react-markdown'
import CustomLink from './components/CustomLink'

ReactDOM.render(
  <ReactMarkdown
    components={{
      // Use `CustomLink` instead of anchor element `a`
      a: CustomLink,
    }}
  >
    [Retool](https://retool.com)
  </ReactMarkdown>,
  document.querySelector('#content')
)
import Link from 'next/link'
export default function CustomLink({ as, href, ...otherProps }) {
  return (
      <Link as={as} href={href}>
        <a {...otherProps} />
      </Link>
  )
}

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 (opens in a new tab) 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 (opens in a new tab) 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 (opens in a new tab) 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:

PlainText

import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'

const input = `<div className=”custom">

## JavaScript code

</div>`

ReactDom.render(
  <ReactMarkdown rehypePlugins={[rehypeRaw]} children={input} />,
  document.body
)

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 (opens in a new tab) plugin to support GitHub flavored markdown. That means you can use GitHub syntax to add autolink literals, footnotes, strike-through, tables, and task lists.

PlainText

 ~~I love jamstack~~                 
https://www.thisbecomesalink.com
* [x] yet another
* [ ] to do

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.

Published

Category

Stories