Enhanced React Markdown with smart line break handling - Supports tables, lists, and automatic paragraph formatting
npm install rwkv-markdown本项目基于 react-markdown 进行二次开发,旨在增强对 RWKV G1 系列大模型输出内容的格式兼容性与渲染稳定性。若无需特殊模型兼容支持,建议直接使用 官方原生版本。
[![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.
* [x] [safe][section-security] by default
(no dangerouslySetInnerHTML or XSS attacks)
* [x] [components][section-components]
(pass your own component to use instead of for ## hi)
* [x] [plugins][section-plugins]
(many plugins you can pick and choose from)
* [x] [compliant][section-syntax]
(100% to CommonMark, 100% to GFM with a plugin)
- react-markdown
- Feature highlights
- Contents
- What is this?
- When should I use this?
- Install
- Use
- API
- Markdown
- Parameters
- Returns
- MarkdownAsync
- Parameters
- Returns
- MarkdownHooks
- Parameters
- Returns
- defaultUrlTransform(url)
- Parameters
- Returns
- AllowElement
- Parameters
- Returns
- Components
- Type
- ExtraProps
- Fields
- HooksOptions
- Extends
- Fields
- Options
- Fields
- UrlTransform
- Parameters
- Returns
- Examples
- Use a plugin
- Use a plugin with options
- Use custom components (syntax highlight)
- Use remark and rehype plugins (math)
- Plugins
- Syntax
- Compatibility
- Architecture
- Appendix A: HTML in markdown
- Appendix B: Components
- Appendix C: line endings in markdown (and JSX)
- Security
- Related
- Contribute
- License
This 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.
* to learn markdown, see this [cheatsheet and tutorial][commonmark-help]
* to try out 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]:
``sh`
npm install react-markdown
In Deno with [esm.sh][esmsh]:
`js`
import Markdown from 'https://esm.sh/react-markdown@10'
In browsers with [esm.sh][esmsh]:
`html`
A basic hello world:
`js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
const markdown = '# Hi, Pluto!'
createRoot(document.body).render(
`
Show equivalent JSX
`js`
Hi, Pluto!
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):
`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(
)
`
Show equivalent JSX
`js`
Just a link: www.nasa.gov.
This package exports the identifiers
[MarkdownAsync][api-markdown-async],MarkdownHooks
[][api-markdown-hooks],defaultUrlTransform
and
[][api-default-url-transform].Markdown
The default export is [][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],UrlTransform
and
[][api-url-transform].
Component to render markdown.
This is a synchronous component.
When using async plugins,
see [MarkdownAsync][api-markdown-async] orMarkdownHooks
[][api-markdown-hooks].
###### Parameters
* options ([Options][api-options])
— props
###### Returns
React element (ReactElement).
Component 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].
###### Parameters
* options ([Options][api-options])
— props
###### Returns
Promise to a React element (Promise).
Component to render markdown with support for async plugins through hooks.
This uses useEffect and useState hooks.MarkdownAsync
Hooks run on the client and do not immediately render something.
For async support on the server,
see [][api-markdown-async].
###### Parameters
* options ([Options][api-options])
— props
###### Returns
React node (ReactNode).
Make a URL safe.
This follows how GitHub works.
It allows the protocols http, https, irc, ircs, mailto, and xmpp,/something
and URLs relative to the current protocol (such as ).
###### Parameters
* url (string)
— URL
###### Returns
Safe URL (string).
Filter elements (TypeScript type).
###### Parameters
* node ([Element from hast][github-hast-element])index
— element to check
* (number | undefined)element
— index of in parentparent
* ([Node from hast][github-hast-nodes])element
— parent of
###### Returns
Whether to allow element (boolean, optional).
Map tag names to components (TypeScript type).
###### Type
`ts
import type {ExtraProps} from 'react-markdown'
import type {ComponentProps, ElementType} from 'react'
type Components = {
[Key in Extract
}
`
Extra fields we pass to components (TypeScript type).
###### Fields
* node ([Element from hast][github-hast-element], optional)
— original node
Configuration for [MarkdownHooks][api-markdown-hooks] (TypeScript type);Options
extends the regular [][api-options] with a fallback prop.
###### Extends
[Options][api-options].
###### Fields
* fallback (ReactNode, optional)
— content to render while the processor processing the markdown
Configuration (TypeScript type).
###### Fields
* allowElement ([AllowElement][api-allow-element], optional)allowedElements
— filter elements;
/ disallowedElements is used firstallowedElements
* (Array, default: all tag names)disallowedElements
— tag names to allow;
cannot combine w/ children
* (string, optional)components
— markdown
* ([Components][api-components], optional)disallowedElements
— map tag names to components
* (Array, default: [])allowedElements
— tag names to disallow;
cannot combine w/ rehypePlugins
* (Array, optional)remarkPlugins
— list of [rehype plugins][github-rehype-plugins] to use
* (Array, optional)remarkRehypeOptions
— list of [remark plugins][github-remark-plugins] to use
* Options
([ from remark-rehype][github-remark-rehype-options],remark-rehype
optional)
— options to pass through to skipHtml
* (boolean, default: false)unwrapDisallowed
— ignore HTML in markdown completely
* (boolean, default: false)strong
— extract (unwrap) what’s in disallowed elements;
normally when say is not allowed, it and it’s children are dropped,unwrapDisallowed
with the element itself is replaced by its childrenurlTransform
* ([UrlTransform][api-url-transform], default:defaultUrlTransform
[][api-default-url-transform])
— change URLs
Transform URLs (TypeScript type).
###### Parameters
* url (string)key
— URL
* (string, example: 'href')node
— property name
* ([Element from hast][github-hast-element])
— element to check
###### Returns
Transformed 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:
`js
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(
)
`
Show equivalent JSX
` A table:js`
<>
A paragraph with emphasis and strong importance.
A block quote with strikethrough and a URL:{' '}
https://reactjs.org.
todo
done
a
b
>
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:
`js
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}
)
`
Show equivalent JSX
`js`
This ~is not~ strikethrough, but this is!
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]:
`js
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(
components={{
code(props) {
const {children, className, node, ...rest} = props
const match = /language-(\w+)/.exec(className || '')
return match ? (
PreTag="div"
children={String(children).replace(/\n$/, '')}
language={match[1]}
style={dark}
/>
) : (
{children}
)
}
}}
/>
)
`
Show equivalent JSX
` Here is some JavaScript code:js`
<>
>
This example shows how a syntax extension
(through [remark-math][github-remark-math])rehype-katex
is used to support math in markdown, and a transform plugin
([][github-rehype-katex]) to render that math.
`jsrehype-katex
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' // does not import the CSS for you
const markdown = The lift coefficient ($C_L$) is a dimensionless coefficient.
createRoot(document.body).render(
{markdown}
)
`
Show equivalent JSX
`js`
The lift coefficient (
) is a dimensionless coefficient.
We use [unified][github-unified],
specifically [remark][github-remark] for markdown and
[rehype][github-rehype] for HTML,
which are tools to transform content with plugins.
Here are three good ways to find plugins:
* [awesome-remark][github-awesome-remark] andawesome-rehype
[][github-awesome-rehype]remark-plugin
— selection of the most awesome projects
* [List of remark plugins][github-remark-plugins] and
[list of rehype plugins][github-rehype-plugins]
— list of all plugins
* [][github-topic-remark-plugin] andrehype-plugin
[][github-topic-rehype-plugin] topics
— any tagged repo on GitHub
react-markdown follows CommonMark, which standardizes the differences between
markdown implementations, by default.
Some syntax extensions are supported through plugins.
We use [micromark][github-micromark] under the hood for our parsing.
See its documentation for more information on markdown, CommonMark, and
extensions.
Projects maintained by the unified collective are compatible with maintained
versions of Node.js.
When we cut a new major release, we drop support for unmaintained versions of
Node.
This means we try to keep the current release line, react-markdown@10,
compatible with Node.js 16.
They work in all modern browsers (essentially: everything not IE 11).
You can use a bundler (such as esbuild, webpack, or Rollup) to use this package
in your project, and use its options (or plugins) to add support for legacy
browsers.
react-markdown
+----------------------------------------------------------------------------------------------------------------+
| |
| +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
| | | | | | | | | | | |
markdown-+->+ remark +-mdast->+ remark plugins +-mdast->+ remark-rehype +-hast->+ rehype plugins +-hast->+ components +-+->react elements
| | | | | | | | | | | |
| +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
| |
+----------------------------------------------------------------------------------------------------------------+
To understand what this project does, it’s important to first understand what
unified does: please read through the [unifiedjs/unified][github-unified]
readme
(the part until you hit the API section is required reading).
react-markdown is a unified pipeline — wrapped so that most folks don’t need
to directly interact with unified.
The processor goes through these steps:
* parse markdown to mdast (markdown syntax tree)
* transform through remark (markdown ecosystem)
* transform mdast to hast (HTML syntax tree)
* transform through rehype (HTML ecosystem)
* render hast to React with components
react-markdown typically escapes HTML (or ignores it, with skipHtml)
because it is dangerous and defeats the purpose of this library.
However, if you are in a trusted environment (you trust the markdown), and
can spare the bundle size (±60kb minzipped), then you can use
[rehype-raw][github-rehype-raw]:
`js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
const markdown =
Some emphasis and strong!
createRoot(document.body).render(
{markdown}
)
`
Show equivalent JSX
`js
Some emphasis and strong!
`Note: HTML in markdown is still bound by how [HTML works in
CommonMark][commonmark-html].
Make sure to use blank lines around block-level HTML that again contains
markdown!
Appendix B: Components
You can also change the things that come from markdown:
`js
components={{
// Map h1 (# heading) to use h2s.
h1: 'h2',
// Rewrite ems (like so) to i with a red foreground color.
em(props) {
const {node, ...rest} = props
return
}
}}
/>
`The keys in components are HTML equivalents for the things you write with
markdown (such as
h1 for # heading).
Normally, in markdown, those are: a, blockquote, br, code, em, h1,
h2, h3, h4, h5, h6, hr, img, li, ol, p, pre, strong, and
ul.
With [remark-gfm][github-remark-gfm],
you can also use del, input, table, tbody, td, th, thead, and tr.
Other remark or rehype plugins that add support for new constructs will also
work with react-markdown.The props that are passed are what you probably would expect: an
a (link) will
get href (and title) props, and img (image) an src, alt and title,
etc.Every component will receive a
node.
This is the original [Element from hast][github-hast-element] element being
turned into a React element.Appendix C: line endings in markdown (and JSX)
You might have trouble with how line endings work in markdown and JSX.
We recommend the following, which solves all line ending problems:
`js
// If you write actual markdown in your code, put your markdown in a variable;
// do not indent markdown:
const markdown = // Pass the value as an expression as an only child:
const result = {markdown}
`👆 That works.
Read on for what doesn’t and why that is.
You might try to write markdown directly in your JSX and find that it **does
not** work:
`js
# Hi This is not a paragraph.
`The is because in JSX the whitespace (including line endings) is collapsed to
a single space.
So the above example is equivalent to:
`js
# Hi This is not a paragraph.
`Instead, to pass markdown to
Markdown, you can use an expression:
with a template literal:`js
{ This is a paragraph.}`
Template literals have another potential problem, because they keep whitespace
(including indentation) inside them.
That means that the following does not turn into a heading:
`js
# This is not a heading, it’s an indented code block}`
Use of react-markdown is secure by default.urlTransform
Overwriting to something insecure will open you up to XSSremarkPlugins
vectors.
Furthermore, the , rehypePlugins, and components you use may
be insecure.
To make sure the content is completely safe, even after what plugins do,
use [rehype-sanitize][github-rehype-sanitize].
It lets you define your own schema of what is and isn’t allowed.
* [MDX][github-mdx]remark-gfm
— JSX in markdown
* [][github-remark-gfm]react-remark
— add support for GitHub flavored markdown support
* [][github-react-remark]rehype-react
— hook based alternative
* [][github-rehype-react]
— turn HTML into React elements
See [contributing.md][health-contributing] in [remarkjs/.github][health]support.md`][health-support] for ways to get help.
for ways to get started.
See [
This project has a [code of conduct][health-coc].
By interacting with this repository, organization, or community you agree to
abide by its terms.
[MIT][file-license] © [Espen Hovlandsdal][author]
[api-allow-element]: #allowelement
[api-components]: #components
[api-default-url-transform]: #defaulturltransformurl
[api-extra-props]: #extraprops
[api-hooks-options]: #hooksoptions
[api-markdown]: #markdown
[api-markdown-async]: #markdownasync
[api-markdown-hooks]: #markdownhooks
[api-options]: #options
[api-url-transform]: #urltransform
[author]: https://espen.codes/
[badge-build-image]: https://github.com/remarkjs/react-markdown/workflows/main/badge.svg
[badge-build-url]: https://github.com/remarkjs/react-markdown/actions
[badge-coverage-image]: https://img.shields.io/codecov/c/github/remarkjs/react-markdown.svg
[badge-coverage-url]: https://codecov.io/github/remarkjs/react-markdown
[badge-downloads-image]: https://img.shields.io/npm/dm/react-markdown.svg
[badge-downloads-url]: https://www.npmjs.com/package/react-markdown
[badge-size-image]: https://img.shields.io/bundlejs/size/react-markdown
[badge-size-url]: https://bundlejs.com/?q=react-markdown
[commonmark-help]: https://commonmark.org/help/
[commonmark-html]: https://spec.commonmark.org/0.31.2/#html-blocks
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[esmsh]: https://esm.sh
[file-license]: license
[github-awesome-rehype]: https://github.com/rehypejs/awesome-rehype
[github-awesome-remark]: https://github.com/remarkjs/awesome-remark
[github-conorhastings]: https://github.com/conorhastings
[github-hast-element]: https://github.com/syntax-tree/hast#element
[github-hast-nodes]: https://github.com/syntax-tree/hast#nodes
[github-io-react-markdown]: https://remarkjs.github.io/react-markdown/
[github-mdx]: https://github.com/mdx-js/mdx/
[github-micromark]: https://github.com/micromark/micromark
[github-react-remark]: https://github.com/remarkjs/react-remark
[github-react-syntax-highlighter]: https://github.com/react-syntax-highlighter/react-syntax-highlighter
[github-rehype]: https://github.com/rehypejs/rehype
[github-rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex
[github-rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
[github-rehype-raw]: https://github.com/rehypejs/rehype-raw
[github-rehype-react]: https://github.com/rehypejs/rehype-react
[github-rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
[github-remark]: https://github.com/remarkjs/remark
[github-remark-gfm]: https://github.com/remarkjs/remark-gfm
[github-remark-math]: https://github.com/remarkjs/remark-math
[github-remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
[github-remark-rehype-options]: https://github.com/remarkjs/remark-rehype#options
[github-topic-rehype-plugin]: https://github.com/topics/rehype-plugin
[github-topic-remark-plugin]: https://github.com/topics/remark-plugin
[github-unified]: https://github.com/unifiedjs/unified
[health]: https://github.com/remarkjs/.github
[health-coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md
[health-contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md
[health-support]: https://github.com/remarkjs/.github/blob/main/support.md
[npm-install]: https://docs.npmjs.com/cli/install
[react]: http://reactjs.org
[section-components]: #appendix-b-components
[section-plugins]: #plugins
[section-security]: #security
[section-syntax]: #syntax
[typescript]: https://www.typescriptlang.org