Open-source project
remarkjs/react-markdown avatar
remarkjs/react-markdown

react-markdown: rendering markdown as React elements, not HTML strings

Markdown component for React

15,893 stars920 forksJavaScriptMIT

At a glance

What is it?
react-markdown is an MIT-licensed React component that turns a markdown string into React elements through unified, remark and rehype. Here is how it works, how to install it, and where it stops being the right tool.
Who is it for?
Adopt react-markdown when you render user-authored or model-generated markdown inside React and want element-level control instead of dangerouslySetInnerHTML. Do not adopt it when you need JavaScript and JSX inside the markdown files, which is the MDX case, or when you need a full editor rather than a renderer.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem react-markdown solves for React applications

The common way to show markdown in a browser is to convert it to an HTML string and hand that string to the DOM. In React that means dangerouslySetInnerHTML, which bypasses React's own rendering path and puts sanitisation in your hands. The README lists this as the first feature highlight: react-markdown is "safe by default (no dangerouslySetInnerHTML or XSS attacks)". The component instead produces React elements, so the output participates in reconciliation like any other JSX.

The second problem is control. If you want a code block to render as a syntax-highlighted component rather than a plain pre element, or a link to route through your own navigation, an HTML string gives you nothing to hook into. react-markdown accepts a components map that swaps elements for your own components; the README's example is passing your own component "instead of <h2> for ## hi".

This is aimed at application developers, not at people writing markdown. The README is explicit that the package "focusses on making it easy for beginners to safely use markdown in React", and points people who already know unified at react-remark or rehype-react instead.

How the markdown string becomes a React tree

The pipeline is unified, with remark handling markdown and rehype handling HTML. Markdown text goes in, a syntax tree comes out, and React elements are built from that tree. Because the result is a virtual DOM rather than an HTML string, the README notes that "React only replaces what changed, from a syntax tree".

Plugins attach at the remark or rehype stage, which is why the package can claim 100% CommonMark compliance and 100% GFM compliance once a plugin is added. The syntax section of the README covers this split; the plugin list is what you pick from.

There are three exported components, and the difference between them is where the work happens. Markdown is synchronous. MarkdownAsync supports async plugins through async/await and returns a promise to a React element, but the README says components returning promises are supported on the server. MarkdownHooks supports async plugins on the client through useEffect and useState, and the README warns that hooks "run on the client and do not immediately render something". Choosing between them is the main architectural decision in a react-markdown integration.

Installing react-markdown and rendering a first document

The package is ESM only and needs Node.js version 16 or later. The README's install command is npm.

bash
npm install react-markdown

A minimal render takes a markdown string and mounts it. This is the README's hello world, adapted to a container element:

js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'

const markdown = '# Hi, *Pluto*!'

createRoot(document.body).render(<Markdown>{markdown}</Markdown>)

The README states the equivalent JSX is an h1 containing the text "Hi, " followed by an em element around "Pluto". If you see a literal asterisk instead, the component is not the thing rendering that string.

Plain CommonMark will not give you tables, task lists, strikethrough, footnotes or bare URLs. Those come from remark-gfm, passed through the remarkPlugins prop:

js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'

const markdown = `Just a link: www.nasa.gov.`

createRoot(document.body).render(
  <Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
)

The README gives the equivalent output as a paragraph containing an anchor whose href is http://www.nasa.gov. Note that the visible text stays as the bare domain; only the href is expanded. Install remark-gfm as a separate package before this example runs.

URL handling, HTML in markdown, and what components cannot do

The package exports defaultUrlTransform, described as following how GitHub works. It allows the protocols http, https, irc, ircs, mailto and xmpp, plus URLs relative to the current protocol such as /something. Anything else is stripped. That is a deliberate boundary, and it is the one most likely to surprise you: a data: or custom-scheme link in your content will not survive unless you replace the urlTransform prop with your own function. The README documents the UrlTransform type for exactly that, but it does not document rollback or recovery behaviour for content that has already been filtered.

HTML embedded in markdown is a separate matter. The README carries an appendix titled "HTML in markdown", which is a signal that raw HTML is not simply passed through. If your content depends on inline HTML for layout, expect to handle it through rehype plugins rather than assuming it renders.

Custom components are the other half of the API, and they have a constraint worth reading before you build on them. Components maps tag names to components, and the AllowElement type is a filter that receives a hast element, its index and its parent, and returns whether to allow it. Filtering happens on the tree, not on the markdown text, so an allowElement callback has to reason about node types rather than about the source string. The README also notes an ExtraProps type, which is where the extra props passed to your components are typed.

When react-markdown is the wrong choice

The clearest boundary is in the README itself: "If you instead want to use JavaScript and JSX inside markdown files, use MDX." A react-markdown pipeline parses markdown; it does not compile embedded JavaScript. Content that needs imports, expressions or component calls inside the document is out of scope.

The second boundary is async work on the client. If a plugin is asynchronous, plain Markdown will not do, and MarkdownHooks does not render immediately because it depends on useEffect and useState. That produces a first paint without content, which matters for above-the-fold text. MarkdownAsync avoids the client-side gap but the README places promise-returning components on the server.

The third is the renderer-versus-editor distinction. react-markdown takes a string and returns elements. It ships no toolbar, no selection handling, no undo stack. Projects that need authoring rather than display are looking at a different category of package, and the related search traffic for "react markdown editor" points at that gap rather than at this library.

Finally, ESM only is a real constraint. Node.js 16 or later is the stated floor, and a CommonJS-only build chain will need configuration. The README does not describe a CommonJS entry point.

Alternatives and the actual difference in approach

The README names two alternatives for people who already work with unified: react-remark and rehype-react. The difference is where the pipeline lives. react-markdown owns the whole path from markdown string to React element and gives you props to configure it. With rehype-react you wire the unified pipeline yourself and hand the tree to a renderer, which is more code but also more freedom over plugin ordering, tree transforms and intermediate steps. react-remark is described as a modern hooks based alternative, so the async and state handling sits in your component rather than in the package's exported variants.

The other comparison the README draws is MDX, and it is not a like-for-like swap. MDX compiles markdown files that may contain JavaScript and JSX. react-markdown renders a string at runtime. If your content is authored by users or generated at request time, MDX's compile step is a different kind of cost than react-markdown's parse-and-render.

Against libraries that produce an HTML string, the difference is the rendering model rather than the feature list. An HTML-string renderer gives you one insertion point and leaves sanitisation to you; react-markdown gives you a tree and a components map, and the README's safety claim rests on never calling dangerouslySetInnerHTML at all.

Maintenance, licence and upgrade cost

react-markdown is MIT licensed, with the licence file at the repository root. For most applications that means the obligations are the standard ones for a permissive licence: keep the copyright notice and licence text with distributions. This is not legal advice, and the licence text itself is the authority.

The repository is not archived, and the last push was on 2026-09-01. The most recent release listed is 10.1.0 on 2025-03-07, following 10.0.1 and 10.0.0 in the same year. The 10.0.0 line is the one to check if you are upgrading from 9.x, because the package is ESM only and that is a breaking change for CommonJS consumers. The changelog at the repository root is where the per-release detail lives.

Upgrade cost is mostly the plugin surface, not the component. Plugins are separate packages with their own release cadence, so a react-markdown bump and a remark-gfm bump are two decisions. The README's compatibility section is the place to check the supported React versions; the package lists React and react-dom as peer dependencies, so a React major upgrade is a coordinated change rather than a drop-in one.

Editorial conclusion

Adopt react-markdown when you render user-authored or model-generated markdown inside React and want element-level control instead of dangerouslySetInnerHTML. Do not adopt it when you need JavaScript and JSX inside the markdown files, which is the MDX case, or when you need a full editor rather than a renderer. Before committing, verify three things: that your bundler and Node version handle an ESM-only package, that React 18 or 19 is in place, and that every URL scheme your content relies on survives defaultUrlTransform.

Frequently asked questions

What is react-markdown used for?

It renders a string of markdown as React elements, so the output goes through React's normal rendering path instead of dangerouslySetInnerHTML. The README describes it as a React component that can be given markdown and will safely render it to React elements.

Is react-markdown safe?

The README lists safety by default as the first feature highlight, stating there is no dangerouslySetInnerHTML and no XSS attack surface from that path. URL handling is also filtered: defaultUrlTransform allows http, https, irc, ircs, mailto and xmpp, plus URLs relative to the current protocol.

How do I install react-markdown?

Install it with npm install react-markdown. The package is ESM only and the README states Node.js version 16 or later is required.

How do I use react-markdown in a component?

Import Markdown from react-markdown and pass the markdown string as children, as in <Markdown>{markdown}</Markdown>. The README's hello world renders the string '# Hi, *Pluto*!' through createRoot and produces an h1 with an em element inside it.

How does react-markdown compare with MDX?

The README draws the line at JavaScript and JSX inside markdown files: if that is what you need, it points to MDX. react-markdown parses markdown and builds React elements, while MDX compiles documents that can contain JavaScript and JSX.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. remarkjs/react-markdown on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/remarkjs-react-markdown.svg)](https://hysenlabs.com/projects/remarkjs-react-markdown)