A markdown editor with preview, implemented with React.js and TypeScript.
npm install @uiw/react-md-editorA simple markdown editor with preview, implemented with React.js and TypeScript. This React Component aims to provide a simple Markdown editor with syntax highlighting support. This is based on textarea encapsulation, so it does not depend on any modern code editors such as Acs, CodeMirror, Monaco etc.
- 📑 Indent line or selected text by pressing tab key, with customizable indentation.
- ♻️ Based on textarea encapsulation, does not depend on any modern code editors.
- 🚧 Does not depend on the uiw component library.
- 🚘 Automatic list on new lines.
- 😻 GitHub flavored markdown support.
- 🌒 Support dark-mode/night-mode @v3.11.0+.
- 💡 Support next.js, Use examples in next.js.
- Line/lines duplication (Ctrl+D) and movement (Alt+UpArrow/DownArrow) @v3.24.0+.
``bash`
npm i @uiw/react-md-editor
or
`bash`
yarn add @uiw/react-md-editor



`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';
export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
return (
$3
The package exposes the necessary utilities to build a headless markdown editor with your own UI. This example creates a simple textarea that supports markdown keyboard shortcuts and correct handling of newlines.
`jsx mdx:preview
import React from "react";
import {
handleKeyDown,
shortcuts,
TextAreaCommandOrchestrator,
getCommands,
} from '@uiw/react-md-editor';export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
const textareaRef = React.useRef(null);
const orchestratorRef = React.useRef(null);
React.useEffect(() => {
if (textareaRef.current) {
orchestratorRef.current = new TextAreaCommandOrchestrator(textareaRef.current);
}
}, []);
const onKeyDown = (e) => {
handleKeyDown(e, 2, false);
if (orchestratorRef.current) {
shortcuts(e, getCommands(), orchestratorRef.current);
}
};
return (
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={onKeyDown}
style={{ width: '100%', height: 200, padding: 10 }}
/>
);
}
`$3
Supports for CSS Style
Use HTML comments
to let Markdown support style customization.`markdown
Title
Markdown Supports Style
`Ignore content display via HTML comments
Shown in GitHub readme, excluded in HTML.
`markdown
Hello World
Hello World
Good!
`Output:
`html
Hello World
Good!
`$3
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
import React from "react";
import MDEditor from '@uiw/react-md-editor';
import rehypeSanitize from "rehype-sanitize";export default function App() {
const [value, setValue] = React.useState(
Hello world!!! );
return (
value={value}
onChange={setValue}
previewOptions={{
rehypePlugins: [[rehypeSanitize]],
}}
/>
);
}
`$3
The following example can help you _exclude code highlighting code_ from being included in the bundle.
@uiw/react-md-editor/nohighlight component does not contain the ~~rehype-prism-plus~~ code highlighting package, ~~highlightEnable~~, ~~showLineNumbers~~ and ~~highlight line~~ functions will no longer work. (#586)`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor/nohighlight';const code =
Hello world!!!\\js
function demo() {}
\\\export default function App() {
const [value, setValue] = React.useState(code);
return (
value={value}
onChange={setValue}
/>
);
}
`$3
"Below is an example that sets the
placeholder for the editor and defines the maximum input character length as 10 characters."`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';export default function App() {
const [value, setValue] = React.useState("");
return (
value={value}
onChange={setValue}
textareaProps={{
placeholder: 'Please enter Markdown text',
maxLength: 10
}}
/>
);
}
`$3

`jsx mdx:preview
import React, { useState } from "react";
import MDEditor, { commands } from '@uiw/react-md-editor';const title3 = {
name: 'title3',
keyCommand: 'title3',
buttonProps: { 'aria-label': 'Insert title3' },
icon: (
),
execute: (state, api) => {
let modifyText =
### ${state.selectedText}\n;
if (!state.selectedText) {
modifyText = ### ;
}
api.replaceSelection(modifyText);
},
};const title2 = {
name: 'title2',
keyCommand: 'title2',
render: (command, disabled, executeCommand) => {
return (
aria-label="Insert title2"
disabled={disabled}
onClick={(evn) => {
// evn.stopPropagation();
executeCommand(command, command.groupName)
}}
>
)
},
execute: (state, api) => {
let modifyText =
## ${state.selectedText}\n;
if (!state.selectedText) {
modifyText = ## ;
}
api.replaceSelection(modifyText);
},
}function SubChildren({ close, execute, getState, textApi, dispatch }) {
const [value, setValue] = useState('')
const insert = () => {
console.log('value:::', value)
textApi.replaceSelection(value)
}
return (
My Custom Toolbar
setValue(e.target.value)} />
type="button"
onClick={() => {
dispatch({ $value: '~~~~~~' })
console.log('> execute: >>>>>', getState())
}}
>
State
);
}const subChild = {
name: 'update',
groupName: 'update',
icon: (
),
children: (props) => ,
execute: (state, api) => {
console.log('>>>>>>update>>>>>', state)
},
buttonProps: { 'aria-label': 'Insert title'}
}
export default function App() {
const [value, setValue] = React.useState("Hello Markdown!
Tab key uses default behavior");
return (
value={value}
onChange={setValue}
commands={[
// Custom Toolbars
title3, title2,
commands.group([commands.title1, commands.title2, commands.title3, commands.title4, commands.title5, commands.title6], {
name: 'title',
groupName: 'title',
buttonProps: { 'aria-label': 'Insert title'}
}),
commands.divider,
commands.group([], subChild),
]}
/>
);
}
`Customize the toolbar with
commands and extraCommands props.`jsx mdx:preview
import React from "react";
import MDEditor, { commands } from '@uiw/react-md-editor';export default function App() {
const [value, setValue] = React.useState("Hello Markdown!
Tab key uses default behavior");
return (
value={value}
onChange={setValue}
preview="edit"
commands={[
commands.codeEdit, commands.codePreview
]}
extraCommands={[
commands.group([commands.title1, commands.title2, commands.title3, commands.title4, commands.title5, commands.title6], {
name: 'title',
groupName: 'title',
buttonProps: { 'aria-label': 'Insert title'}
}),
commands.divider,
commands.group([], {
name: 'update',
groupName: 'update',
icon: (
),
children: ({ close, execute, getState, textApi }) => {
return (
My Custom Toolbar
);
},
execute: (state, api) => {
console.log('>>>>>>update>>>>>', state)
},
buttonProps: { 'aria-label': 'Insert title'}
}),
commands.divider, commands.fullscreen
]}
/>
);
}
`re-render
toolbar element.`jsx mdx:preview
import React from "react";
import MDEditor, { commands } from '@uiw/react-md-editor';export default function App() {
const [value, setValue] = React.useState("Hello Markdown!
Tab key uses default behavior");
return (
value={value}
onChange={setValue}
preview="edit"
components={{
toolbar: (command, disabled, executeCommand) => {
if (command.keyCommand === 'code') {
return (
aria-label="Insert code"
disabled={disabled}
onClick={(evn) => {
evn.stopPropagation();
executeCommand(command, command.groupName)
}}
>
Code
)
}
}
}}
/>
);
}
`Custom Preview Command Tool

`jsx mdx:preview
import React, { useContext } from "react";
import MDEditor, { commands, EditorContext } from "@uiw/react-md-editor";const Button = () => {
const { preview, dispatch } = useContext(EditorContext);
const click = () => {
dispatch({
preview: preview === "edit" ? "preview" : "edit"
});
};
if (preview === "edit") {
return (
);
}
return (
);
};
const codePreview = {
name: "preview",
keyCommand: "preview",
value: "preview",
icon:
};
const Disable = () => {
const { preview, dispatch } = useContext(EditorContext);
return (
)
}
const customButton = {
name: "disable",
keyCommand: "disable",
value: "disable",
icon:
}
export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
return (
The system automatically sets the theme
value={value}
preview="edit"
extraCommands={[codePreview, customButton, commands.fullscreen]}
onChange={(val) => setValue(val)}
/>
);
}
`Add Help Command Tool

`jsx mdx:preview
import React, { useContext } from "react";
import MDEditor, { commands } from "@uiw/react-md-editor";const help = {
name: "help",
keyCommand: "help",
buttonProps: { "aria-label": "Insert help" },
icon: (
),
execute: (state, api) => {
window.open("https://www.markdownguide.org/basic-syntax/", "_blank");
}
};
export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
return (
value={value}
preview="edit"
commands={[...commands.getCommands(), help]}
onChange={(val) => setValue(val)}
/>
);
}
`commands-cn for internationalization.`jsx mdx:preview
import React, { useContext } from "react";
import MDEditor, { commands } from "@uiw/react-md-editor";
import { getCommands, getExtraCommands } from "@uiw/react-md-editor/commands-cn";export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
return (
value={value}
preview="edit"
commands={[...getCommands()]}
extraCommands={[...getExtraCommands()]}
onChange={(val) => setValue(val)}
/>
);
}
`Set to support editor only
`jsx mdx:preview
import React, { useContext } from "react";
import MDEditor, { commands, EditorContext } from "@uiw/react-md-editor";export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
return (
value={value}
preview="edit"
extraCommands={[commands.fullscreen]}
onChange={(val) => setValue(val)}
/>
);
}
`$3


`css
body .w-md-editor-text-pre > code,
body .w-md-editor-text-input {
font-size: 23px !important;
line-height: 24px !important;
}
`$3
The initial height can be adjusted through
minHeight={100}. Dragbar will automatically expire. You can hide the drag button through visibleDragbar={false}`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';export default function App() {
const [value, setValue] = React.useState("Hello world!!!");
return (
value={value}
height="100%"
// minHeight={50}
visibleDragbar={false}
onChange={setValue}
/>
);
}
`$3
`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';export default function App() {
const [value, setValue] = React.useState("Hello world!!! ");
return (
value={value}
height="100%"
previewOptions={{
disallowedElements: ['style'],
}}
visibleDragbar={false}
onChange={setValue}
/>
);
}
`$3
`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';const textSample =
# Welcome to the Markdown Editor!This is a sample of the React Markdown Editor.
\\\javascript\
function hello() {
console.log("Hello, world!");
}
\\
Visit uiwjs/react-md-editor for more information.;
export default function App() {
const [value, setValue] = React.useState(textSample);
return (
$3

`jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';export default function App() {
return (
);
}
`$3
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web, We perform math rendering through
KaTeX.The following example is preview in CodeSandbox.

> ⚠️ Upgrade v2 to v3 d025430
`bash
npm install katex
``jsx mdx:preview
import React from "react";
import MDEditor from '@uiw/react-md-editor';
import { getCodeString } from 'rehype-rewrite';
import katex from 'katex';
import 'katex/dist/katex.css';const mdKaTeX =
This is to display the \$\$\c = \\pm\\sqrt{a^2 + b^2}\$\$\\\\KaTeX\
c = \\pm\\sqrt{a^2 + b^2}
\\;
export default function App() {
const [value, setValue] = React.useState(mdKaTeX);
return (
onChange={(val) => setValue(val)}
previewOptions={{
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};
},
},
}}
/>
);
}
`

`tsx mdx:preview
import React, { useState } from "react";
import MDEditor, { commands, type ICommand, TextState, TextAreaTextApi } from "@uiw/react-md-editor";
import domToImage from "dom-to-image";
const textToImage: ICommand = {
name: "Text To Image",
keyCommand: "text2image",
buttonProps: { "aria-label": "Insert title3" },
icon: (
),
execute: (state: TextState, api: TextAreaTextApi) => {
const dom = document.getElementsByClassName("gooooooooo")[0];
if (dom) {
domToImage.toJpeg(dom, {}).then((dataUrl) => {
const link = document.createElement("a");
link.download = "image.jpg";
link.href = dataUrl;
link.click();
});
}
}
};
export default function App() {
const [value, setValue] = useState('Hello world!!!');
console.log('value::', value)
return (
$3
Using mermaid to generation of diagram and flowchart from text in a similar manner as markdown

`bash
npm install mermaid
``jsx mdx:preview
import React, { useState, useRef, useEffect, Fragment, useCallback } from "react";
import MDEditor from "@uiw/react-md-editor";
import { getCodeString } from 'rehype-rewrite';
import mermaid from "mermaid";const mdMermaid =
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 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 = children
? getCodeString(props.node.children)
: children[0] || "";
useEffect(() => {
if (container && isMermaid && demoid.current && code) {
mermaid
.render(demoid.current, code)
.then(({ svg, bindFunctions }) => {
container.innerHTML = svg;
if (bindFunctions) {
bindFunctions(container);
}
})
.catch((error) => {
console.log("error:", error);
});
}
}, [container, isMermaid, code, demoid]);
const refElement = useCallback((node) => {
if (node !== null) {
setContainer(node);
}
}, []);
if (isMermaid) {
return (
);
}
return {children};
};
export default function App() {
const [value, setValue] = useState(mdMermaid);
return (
textareaProps={{
placeholder: "Please enter Markdown text"
}}
height={500}
value={value}
previewOptions={{
components: {
code: Code
}
}}
/>
);
}
`
Use examples in nextjs. #52 #224

 


`bash`
npm install next-remove-imports
npm install @uiw/react-md-editor@v3.6.0
`js`
// next.config.js
const removeImports = require('next-remove-imports')();
module.exports = removeImports({});
`jsx
import "@uiw/react-md-editor/markdown-editor.css";
import "@uiw/react-markdown-preview/markdown.css";
import dynamic from "next/dynamic";
import { useState } from "react";
import * as commands from "@uiw/react-md-editor/commands"
const MDEditor = dynamic(
() => import("@uiw/react-md-editor"),
{ ssr: false }
);
function HomePage() {
const [value, setValue] = useState("Hello world!!!");
return (
export default HomePage;
`
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. Setting theme styles with data-color-mode="light".
`html`
- value: string: The Markdown value.onChange?: (value?: string, event?: React.ChangeEvent
- : Event handler for the onChange event.onHeightChange?: ((value?: CSSProperties['height'], oldValue?: CSSProperties['height'], state?: ContextStore)
- : editor height change listener.onStatistics?: (data: Statistics) => void;
- Some data on the statistics editor.commands?: ICommand[]
- : An array of ICommand, which, each one, contain a commands property. If no commands are specified, the default will be used. Commands are explained in more details below.commandsFilter?: (command: ICommand, isExtra: boolean) => false | ICommand
- : Filter or modify your commands.extraCommands?: ICommand[]
- : Displayed on the right side of the toolbar.autoFocus?: true
- : Can be used to make Markdown Editor focus itself on initialization.autoFocusEnd?: false
- : Can be used to make Markdown Editor focus on the end of text on initialization.previewOptions?: ReactMarkdown.ReactMarkdownProps
- : This is reset @uiw/react-markdown-preview settings.textareaProps?: TextareaHTMLAttributes
- : Set the textarea related props.renderTextarea?: (props, opts) => JSX.Element;
- ~~~~: @deprecated Please use ~~renderTextarea~~ -> components. Use div to replace TextArea or re-render TextArea. #193components
- : re-render textarea/toolbar element. #419textarea
- Use div to replace TextArea or re-render TextAreatoolbar
- Override the default command element. _toolbar_ < _command[].render_preview
- Custom markdown preview. #429height?: number=200
- : The height of the editor. ️⚠️ Dragbar is invalid when height parameter percentage.visibleDragbar?: boolean=true
- : Show drag and drop tool. Set the height of the editor.highlightEnable?: boolean=true
- : Disable editing area code highlighting. The value is false, which increases the editing speed.fullscreen?: boolean=false
- : Show markdown preview.overflow?: boolean=true
- : Disable fullscreen setting body stylespreview?: 'live' | 'edit' | 'preview'
- : Default value live, Show markdown preview.maxHeight?: number=1200
- : Maximum drag height. The visibleDragbar=true value is valid.minHeight?: number=100
- : Minimum drag height. The visibleDragbar=true value is valid.tabSize?: number=2
- : The number of characters to insert when pressing tab key. Default 2 spaces.defaultTabEnable?: boolean=false
- : If false, the tab key inserts a tab character into the textarea. If true, the tab key executes default behavior e.g. focus shifts to next element. hideToolbar?: boolean=false
- : Option to hide the tool bar.enableScroll?: boolean=true
- : Whether to enable scrolling.
1. Install dependencies
`bash`
$ npm install # Installation dependencies
$ npm run build # Compile all package
2. Development @uiw/react-md-editor package:
`bash@uiw/react-md-editor
$ cd corelisten to the component compile and output the .js file
listen for compilation output type .d.ts file
$ npm run watch # Monitor the compiled package `
3. Launch documentation site
`bash``
npm run start
- @uiw/react-textarea-code-editor: A simple code editor with syntax highlighting.
- @uiw/react-md-editor: A simple markdown editor with preview, implemented with React.js and TypeScript.
- @uiw/react-codemirror: CodeMirror component for React. @codemirror
- @uiw/react-monacoeditor: Monaco Editor component for React.
- @uiw/react-markdown-editor: A markdown editor with preview, implemented with React.js and TypeScript.
- @uiw/react-markdown-preview: React component preview markdown text in web browser.
As always, thanks to our amazing contributors!
Made with contributors.
Licensed under the MIT License.