[![Build][badge-build-image]][badge-build-url] [![Coverage][badge-coverage-image]][badge-coverage-url] [![Downloads][badge-downloads-image]][badge-downloads-url] [![Size][badge-size-image]][badge-size-url]
React component to render markdown.
dangerouslySetInnerHTML or XSS attacks)<h2> for ## hi)MarkdownMarkdownAsyncMarkdownHooksdefaultUrlTransform(url)AllowElementComponentsExtraPropsHooksOptionsOptionsUrlTransformThis package is a [React][] component that can be given a string of markdown that it’ll safely render to React elements. You can pass plugins to change how markdown is transformed and pass components that will be used instead of normal HTML elements.
react-markdown, see [our demo][github-io-react-markdown]There are other ways to use markdown in React out there so why use this one?
The three main reasons are that they often rely on dangerouslySetInnerHTML,
have bugs with how they handle markdown, or don’t let you swap elements for
components.
react-markdown builds a virtual DOM, so React only replaces what changed,
from a syntax tree.
That’s supported because we use [unified][github-unified],
specifically [remark][github-remark] for markdown and [rehype][github-rehype]
for HTML,
which are popular tools to transform content with plugins.
This package focusses on making it easy for beginners to safely use markdown in
React.
When you’re familiar with unified, you can use a modern hooks based alternative
[react-remark][github-react-remark] or [rehype-react][github-rehype-react]
manually.
If you instead want to use JavaScript and JSX inside markdown files, use
[MDX][github-mdx].
This package is [ESM only][esm]. In Node.js (version 16+), install with [npm][npm-install]:
npm install react-markdown
In Deno with [esm.sh][esmsh]:
import Markdown from 'https://esm.sh/react-markdown@10'
In browsers with [esm.sh][esmsh]:
<script type="module">
import Markdown from 'https://esm.sh/react-markdown@10?bundle'
</script>
A basic hello world:
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>)
Show equivalent JSX
<h1>
Hi, <em>Pluto</em>!
</h1>
Here is an example that shows how to use a plugin
([remark-gfm][github-remark-gfm],
which adds support for footnotes, strikethrough, tables, tasklists and
URLs directly):
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>
)
Show equivalent JSX
Just a link: <a href="http://www.nasa.gov">www.nasa.gov</a>.
This package exports the identifiers
[MarkdownAsync][api-markdown-async],
[MarkdownHooks][api-markdown-hooks],
and
[defaultUrlTransform][api-default-url-transform].
The default export is [Markdown][api-markdown].
It also exports the additional [TypeScript][] types
[AllowElement][api-allow-element],
[Components][api-components],
[ExtraProps][api-extra-props],
[HooksOptions][api-hooks-options],
[Options][api-options],
and
[UrlTransform][api-url-transform].
MarkdownComponent to render markdown.
This is a synchronous component.
When using async plugins,
see [MarkdownAsync][api-markdown-async] or
[MarkdownHooks][api-markdown-hooks].
options ([Options][api-options])
— propsReact element (ReactElement).
MarkdownAsyncComponent to render markdown with support for async plugins through async/await.
Components returning promises are supported on the server.
For async support on the client,
see [MarkdownHooks][api-markdown-hooks].
options ([Options][api-options])
— propsPromise to a React element (Promise<ReactElement>).
MarkdownHooksComponent to render markdown with support for async plugins through hooks.
This uses useEffect and useState hooks.
Hooks run on the client and do not immediately render something.
For async support on the server,
see [MarkdownAsync][api-markdown-async].
options ([Options][api-options])
— propsReact node (ReactNode).
defaultUrlTransform(url)Make a URL safe.
url (string)
— URLSafe URL (string).
AllowElementFilter elements (TypeScript type).
node ([Element from hast][github-hast-element])
— element to checkindex (number | undefined)
— index of element in parentparent ([Node from hast][github-hast-nodes])
— parent of elementWhether to allow element (boolean, optional).
ComponentsMap tag names to components (TypeScript type).
import type {ExtraProps} from 'react-markdown'
import type {ComponentProps, ElementType} from 'react'
type Components = {
[Key in Extract<ElementType, string>]?: ElementType<ComponentProps<Key> & ExtraProps>
}
ExtraPropsExtra fields we pass to components (TypeScript type).
node ([Element from hast][github-hast-element], optional)
— original nodeHooksOptionsConfiguration for [MarkdownHooks][api-markdown-hooks] (TypeScript type);
extends the regular [Options][api-options] with a fallback prop.
[Options][api-options].
fallback (ReactNode, optional)
— content to render while the processor processing the markdownOptionsConfiguration (TypeScript type).
allowElement ([AllowElement][api-allow-element], optional)
— filter elements;
allowedElements / disallowedElements is used firstallowedElements (Array<string>, default: all tag names)
— tag names to allow;
cannot combine w/ disallowedElementschildren (string, optional)
— markdowncomponents ([Components][api-components], optional)
— map tag names to componentsdisallowedElements (Array<string>, default: [])
— tag names to disallow;
cannot combine w/ allowedElementsrehypePlugins (Array<Plugin>, optional)
— list of [rehype plugins][github-rehype-plugins] to useremarkPlugins (Array<Plugin>, optional)
— list of [remark plugins][github-remark-plugins] to useremarkRehypeOptions
([Options from remark-rehype][github-remark-rehype-options],
optional)
— options to pass through to remark-rehypeskipHtml (boolean, default: false)
— ignore HTML in markdown completelyunwrapDisallowed (boolean, default: false)
— extract (unwrap) what’s in disallowed elements;
normally when say strong is not allowed, it and it’s children are dropped,
with unwrapDisallowed the element itself is replaced by its childrenurlTransform ([UrlTransform][api-url-transform], default:
[defaultUrlTransform][api-default-url-transform])
— change URLsUrlTransformTransform URLs (TypeScript type).
url (string)
— URLkey (string, example: 'href')
— property namenode ([Element from hast][github-hast-element])
— element to checkTransformed URL (string, optional).
This example shows how to use a remark plugin.
In this case, [remark-gfm][github-remark-gfm],
which adds support for strikethrough, tables, tasklists and URLs directly:
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
const markdown = `A paragraph with *emphasis* and **strong importance**.
> A block quote with ~strikethrough~ and a URL: https://reactjs.org.
* Lists
* [ ] todo
* [x] done
A table:
| a | b |
| - | - |
`
createRoot(document.body).render(
<Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
)
Show equivalent JSX
<>
A paragraph with <em>emphasis</em> and <strong>strong importance</strong>.
<blockquote>
A block quote with <del>strikethrough</del> and a URL:{' '}
<a href="https://reactjs.org">https://reactjs.org</a>.
</blockquote>
<ul className="contains-task-list">
<li>Lists</li>
<li className="task-list-item">
<input type="checkbox" disabled /> todo
</li>
<li className="task-list-item">
<input type="checkbox" disabled checked /> done
</li>
</ul>
A table:
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
</table>
</>
This example shows how to use a plugin and give it options.
To do that, use an array with the plugin at the first place, and the options
second.
[remark-gfm][github-remark-gfm] has an option to allow only double tildes for
strikethrough:
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
const markdown = 'This ~is not~ strikethrough, but ~~this is~~!'
createRoot(document.body).render(
<Markdown remarkPlugins={[[remarkGfm, {singleTilde: false}]]}>
{markdown}
</Markdown>
)
Show equivalent JSX
This ~is not~ strikethrough, but <del>this is</del>!
This example shows how you can overwrite the normal handling of an element by
passing a component.
In this case, we apply syntax highlighting with the seriously super amazing
[react-syntax-highlighter][github-react-syntax-highlighter] by
[@conorhastings][github-conorhastings]:
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
// Did you know you can use tildes instead of backticks for code in markdown? ✨
const markdown = `Here is some JavaScript code:
~~~js
console.log('It works!')
~~~
`
createRoot(document.body).render(
<Markdown
children={markdown}
components={{
code(props) {
const {children, className, node, ...rest} = props
const match = /language-(\w+)/.exec(className || '')
return match ? (
<SyntaxHighlighter
{...rest}
PreTag="div"
children={String(children).replace(/\n$/, '')}
language={match[1]}
style={dark}
/>
) : (
<code {...rest} className={className}>
{children}
</code>
)
}
}}
/>
)
Show equivalent JSX
<>
Here is some JavaScript code:
<pre>
<SyntaxHighlighter language="js" style={dark} PreTag="div" children="console.log('It works!')" />
</pre>
</>
This example shows how a syntax extension
(through [remark-math][github-remark-math])
is used to support math in markdown, and a transform plugin
([rehype-katex][github-rehype-katex]) to render that math.
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'
import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
const markdown = `The lift coefficient ($C_L$) is a dimensionless coefficient.`
createRoot(document.body).render(
<Markdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
{markdown}
</Markdown>
)
Show equivalent JSX
```js
The lift coefficient ( <span className="katex-mathml
$ claude mcp add react-markdown \
-- python -m otcore.mcp_server <graph>