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 content for your pages in Markdown. It lets you create rich 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.

Screenshot of react-markdown in action

What is react-markdown?

React-markdown is a React component that safely renders markdown into HTML. 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.

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.

Screenshot of the #of weekly downloads for react-markdown

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.

Using react-markdown

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

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

    Man, imagine how *annoying* it would be to have to write **all** of this using HTML tags

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

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

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

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

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

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:

Using an external .md file

Custom Components

React-markdown also allows you to pass through custom components as markdown targets. 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:

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

      // Use `CustomLink` instead of anchor element `a`
      a: CustomLink,
import Link from 'next/link'
export default function CustomLink({ as, href, ...otherProps }) {
  return (
      <Link as={as} href={href}>
        <a {...otherProps} />

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.


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:

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


  <ReactMarkdown rehypePlugins={[rehypeRaw]} children={input} />,

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.

 ~~I love jamstack~~                 
* [x] yet another
* [ ] to do


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 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.