React component preview markdown text in web browser. The minimal amount of CSS to replicate the GitHub Markdown style.
npm install @uiw/react-markdown-preview
React Markdown Preview
===







React component preview markdown text in web browser. The minimal amount of CSS to replicate the GitHub Markdown style. The current document website is converted using this react component.
- 🌒 Support dark-mode/night-mode. @v4
- 🙆🏼♂️ GitHub style: The markdown content is rendered as close to the way it's rendered on GitHub as possible.
- 🏋🏾♂️ Support GFM (autolink literals, footnotes, strikethrough, tables, tasklists).
- 🍭 Support automatic code block highlight.
- 🐝 Support for defining styles via comment.
- ⛳️ Support for GFM footnotes
- ⛳️ Support for Github Alert
``bash`
$ npm install @uiw/react-markdown-preview --save

`jsx mdx:preview&checkered=0
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';
const source =
> todo: React component preview markdown text.;
export default function Demo() {
return (
)
}
`
`jsx mdx:preview&checkered=0
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';
const source =
;export default function Demo() {
return (
source={source}
style={{ padding: 16 }}
rehypeRewrite={(node, index, parent) => {
if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
parent.children = parent.children.slice(1)
}
}}
/>
);
}
`highlight line
syntax: `jsx {1,4-5}
`jsx mdx:preview
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';const source =
\\js {2}
function () {
console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\\\\\js {2}
function () {
console.log('hello ')
}
\\\;export default function Demo() {
return (
source={source}
style={{ padding: 16 }}
rehypeRewrite={(node, index, parent) => {
if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
parent.children = parent.children.slice(1)
}
}}
/>
);
}
`Show Line Numbers
syntax: `jsx showLineNumbers {1,4-5}
`jsx mdx:preview?background=#fff
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';const source =
\\js showLineNumbers
function () {
console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\\\\\js showLineNumbers {2}
function () {
console.log('hello ')
}
\\\;export default function Demo() {
return (
source={source}
style={{ padding: 16 }}
rehypeRewrite={(node, index, parent) => {
if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
parent.children = parent.children.slice(1)
}
}}
/>
);
}
`Code Highlight
`jsx mdx:preview
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';const source =
\\js
function () {
console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\\\\\js
function () {
console.log('hello ')
}
\\\;export default function Demo() {
return (
);
}
`Remove Code Highlight
The following example can help you _exclude code highlighting code_ from being included in the bundle.
@uiw/react-markdown-preview/nohighlight component does not contain the rehype-prism-plus code highlighting package, showLineNumbers and highlight line functions will no longer work. (#586)`jsx mdx:preview
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview/nohighlight';const source =
\\js
function () {
console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\\\\\js
function () {
console.log('hello ')
}
\\\;export default function Demo() {
return (
source={source}
style={{ padding: 16 }}
rehypeRewrite={(node, index, parent) => {
if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
parent.children = parent.children.slice(1)
}
}}
/>
);
}
`Ignore
Ignore content display via HTML comments, Shown in GitHub readme, excluded in HTML.
`jsx mdx:preview?background=#fff
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';const source =
;export default function Demo() {
return (
source={source}
style={{ padding: 16 }}
rehypeRewrite={(node, index, parent) => {
if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
parent.children = parent.children.slice(1)
}
}}
/>
);
}
``md
Ignored content
`Support Custom KaTeX Preview
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web, We perform math rendering through
KaTeX.`bash
npm install katex
``jsx mdx:preview?background=#fff
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';
import { getCodeString } from 'rehype-rewrite';
import katex from 'katex';
import 'katex/dist/katex.css';const source =
This is to display the \$\$\c = \\pm\\sqrt{a^2 + b^2}\$\$\\\\KaTeX\
c = \\pm\\sqrt{a^2 + b^2}
\\;
export default function Demo() {
const [value, setValue] = React.useState(source);
return (
style={{ padding: 16 }}
components={{
code: ({ children = [], className, ...props }) => {
if (typeof children === 'string' && /^\$\$(.*)\$\$/.test(children)) {
const html = katex.renderToString(children.replace(/^\$\$(.*)\$\$/, '$1'), {
throwOnError: false,
});
return ;
}
const code = props.node && props.node.children ? getCodeString(props.node.children) : children;
if (
typeof code === 'string' &&
typeof className === 'string' &&
/^language-katex/.test(className.toLocaleLowerCase())
) {
const html = katex.renderToString(code, {
throwOnError: false,
});
return ;
}
return {children};
},
}}
/>
);
}
`
Using mermaid to generation of diagram and flowchart from text in a similar manner as markdown

`jsx mdx:preview?background=#fff
import React, { useState, useRef, useEffect, Fragment, useCallback } from "react";
import MarkdownPreview from '@uiw/react-markdown-preview';
import { getCodeString } from 'rehype-rewrite';
import mermaid from "mermaid";
const randomid = () => parseInt(String(Math.random() * 1e15), 10).toString(36);
const Code = ({ inline, children = [], className, ...props }) => {
const demoid = useRef(dome${randomid()});
const [container, setContainer] = useState(null);
const isMermaid = className && /^language-mermaid/.test(className.toLocaleLowerCase());
const code = props.node && props.node.children ? getCodeString(props.node.children) : children[0] || '';
const reRender = async () => {
if (container && isMermaid) {
try {
const str = await mermaid.render(demoid.current, code);
container.innerHTML = str.svg;
} catch (error) {
container.innerHTML = error;
}
}
}
useEffect(() => {
reRender()
}, [container, isMermaid, code, demoid]);
const refElement = useCallback((node) => {
if (node !== null) {
setContainer(node);
}
}, []);
if (isMermaid) {
return (
);
}
return {children};
};
const source = The following are some examples of the diagrams, charts and graphs that can be made using Mermaid and the Markdown-inspired text specific to it.
\\\mermaid\
graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
\\
\\\mermaid\
sequenceDiagram
Alice->>John: Hello John, how are you?
loop Healthcheck
John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts!
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
\\;
// const source =
// \\\mermaid\
// graph TD;
// A-->B;
// A-->C;
// B-->D;
// C-->D;
// \\
// ;
export default function Demo() {
return (
style={{ padding: 16 }}
components={{
code: Code
}}
/>
);
}
`
Please note markdown needs to be sanitized if you do not completely trust your authors. Otherwise, your app is vulnerable to XSS. This can be achieved by adding rehype-sanitize as a plugin.
`jsx mdx:preview&checkered=0
import React from 'react';
import rehypeSanitize from "rehype-sanitize";
import MarkdownPreview from '@uiw/react-markdown-preview';
const source =
Hello world!!!
123
456 ;
const rehypePlugins = [rehypeSanitize];
export default function Demo() {
return (
)
}
`
`typescript
import { ReactMarkdownProps } from 'react-markdown';
import { RehypeRewriteOptions } from 'rehype-rewrite';
type MarkdownPreviewProps = {
prefixCls?: string;
className?: string;
source?: string;
disableCopy?: boolean;
style?: React.CSSProperties;
pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
wrapperElement?: React.DetailedHTMLProps
'data-color-mode'?: 'light' | 'dark';
};
onScroll?: (e: React.UIEvent
onMouseOver?: (e: React.MouseEvent
rehypeRewrite?: RehypeRewriteOptions['rewrite'];
} & ReactMarkdownProps;
`
- source (string, default: '')\className
Markdown to parse
- (string?)\div
Wrap the markdown in a with this class name
This ReactMarkdownProps details. Upgrade react-markdown v9
- children (string, default: '')\className
Markdown to parse
- (string?)\div
Wrap the markdown in a with this class nameskipHtml
- (boolean, default: ~~false~~ -> true )\allowElement
Ignore HTML in Markdown completely
- ((element, index, parent) => boolean?, optional)\allowedElements
Function called to check if an element is allowed (when truthy) or not.
/ disallowedElements is used first!remarkPlugins
- (Array., default: [])\rehypePlugins
List of remark plugins to use.
See the next section for examples on how to pass options
- (Array., default: [])\
List of rehype plugins to use.
See the next section for examples on how to pass options
> [!NOTE]
>
> Upgrade react-markdown ~~v8~~ to v9
The transformImageUri and transformLinkUri were removed.urlTransform
Having two functions is a bit much, particularly because there are more URLs
you might want to change (or which might be unsafe so we make them safe).
And their name and APIs were a bit weird.
You can use the new prop instead to change all your URLs.
The linkTarget option was removed; you should likely not set targets.rehype-external-links
If you want to, use.
The includeElementIndex option was removed, so index is never passed toindex
components.
Write a plugin to pass :
Show example of plugin
`jsx
import {visit} from 'unist-util-visit'
function rehypePluginAddingIndex() {
/**
* @param {import('hast').Root} tree
* @returns {undefined}
*/
return function (tree) {
visit(tree, function (node, index) {
if (node.type === 'element' && typeof index === 'number') {
node.properties.index = index
}
})
}
}
`
The rawSourcePos option was removed, so sourcePos is never passed tonode
components.
All components are passed , so you can get node.position from them.
The sourcePos option was removed, so data-sourcepos is never passed toindex
elements.
Write a plugin to pass :
Show example of plugin
`jsx
import {stringifyPosition} from 'unist-util-stringify-position'
import {visit} from 'unist-util-visit'
function rehypePluginAddingIndex() {
/**
* @param {import('hast').Root} tree
* @returns {undefined}
*/
return function (tree) {
visit(tree, function (node) {
if (node.type === 'element') {
node.properties.dataSourcepos = stringifyPosition(node.position)
}
})
}
}
`
When overwriting components, these props are no longer passed:
* inline on codepre
— create a plugin or use for the blocklevel
* on h1, h2, h3, h4, h5, h6node.tagName
— check insteadchecked
* on litask-list-item
— check class or check props.childrenindex
* on liordered
— create a plugin
* on lidepth
— create a plugin or check the parent
* on ol, ulordered
— create a plugin
* on ol, ulnode.tagName
— check insteadisHeader
* on td, thnode.tagName
— check insteadisHeader
* on tr
— create a plugin or check children
Use HTML comments to let Markdown support style customization.
`markdownTitle
Markdown Supports Style
`
`markdown
Here is a simple footnote[^1]. With some additional text after it.
[^1]: My reference.
`
`markdownHello World
Hello World
Good!
`
Output:
`htmlHello World
Good!
$3
`jsx mdx:preview&checkered=0
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';const source =
> > [!TIP]
> Helpful advice for doing things better or more easily.
> [!IMPORTANT]
> Key information users need to know to achieve their goal.
> [!WARNING]
> Urgent info that needs immediate user attention to avoid problems.
> [!CAUTION]
> Advises about risks or negative outcomes of certain actions.
;
export default function Demo() {
return (
)
}
`
By default, the dark-mode is automatically switched according to the system. If you need to switch manually, just set the data-color-mode="dark" parameter for body.
`html`
`js`
document.documentElement.setAttribute('data-color-mode', 'dark')
document.documentElement.setAttribute('data-color-mode', 'light')
Inherit custom color variables by adding .wmde-markdown-var selector.
`jsx`
const Demo = () => {
return (
)
}
Set the light theme.
`diff`
wrapperElement={{
+ "data-color-mode": "light"
}}
/>
Runs the project in development mode.
`bash`Step 1, run first,
listen to the component compile and output the .js file
listen for compilation output type .d.ts file
listen to the component compile and output the .css file
npm run startStep 2, development mode, listen to compile preview website instance
npm run doc
Builds the app for production to the build folder.
`bash``
npm run build
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
If you need more features-rich Markdown Editor, you can use @uiwjs/react-markdown-editor
- @uiw/react-markdown-editor: A markdown editor with preview, implemented with React.js and TypeScript.
- @uiw/react-md-editor: A simple markdown editor with preview, implemented with React.js and TypeScript.
- @uiw/react-textarea-code-editor: A simple code editor with syntax highlighting.
- @uiw/react-codemirror: CodeMirror component for React. @codemirror
- @uiw/react-monacoeditor: Monaco Editor component for React.
As always, thanks to our amazing contributors!
Made with action-contributors.
Licensed under the MIT License.