Gathering detailed insights and metrics for @uiw/react-md-editor
Gathering detailed insights and metrics for @uiw/react-md-editor
Gathering detailed insights and metrics for @uiw/react-md-editor
Gathering detailed insights and metrics for @uiw/react-md-editor
A simple markdown editor with preview, implemented with React.js and TypeScript.
npm install @uiw/react-md-editor
Typescript
Module System
Node Version
NPM Version
TypeScript (93.46%)
Less (5.06%)
HTML (1.11%)
CSS (0.28%)
JavaScript (0.05%)
Shell (0.04%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
2,485 Stars
903 Commits
180 Forks
13 Watchers
14 Branches
32 Contributors
Updated on Jul 12, 2025
Latest Version
4.0.7
Package Id
@uiw/react-md-editor@4.0.7
Unpacked Size
4.18 MB
Size
1.05 MB
File Count
249
NPM Version
10.8.2
Node Version
20.19.1
Published on
May 22, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
A 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.
textarea
encapsulation, does not depend on any modern code editors.uiw
component library.1npm i @uiw/react-md-editor
or
1yarn add @uiw/react-md-editor
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3 4export default function App() { 5 const [value, setValue] = React.useState("**Hello world!!!**"); 6 return ( 7 <div className="container"> 8 <MDEditor 9 value={value} 10 onChange={setValue} 11 /> 12 <MDEditor.Markdown source={value} style={{ whiteSpace: 'pre-wrap' }} /> 13 </div> 14 ); 15}
Supports for CSS Style
Use HTML comments <!--rehype:xxx-->
to let Markdown support style customization.
1## Title 2<!--rehype:style=display: flex; height: 230px; align-items: center; justify-content: center; font-size: 38px;--> 3 4Markdown Supports **Style**<!--rehype:style=color: red;-->
Ignore content display via HTML comments
Shown in GitHub readme, excluded in HTML.
1# Hello World 2 3<!--rehype:ignore:start-->Hello World<!--rehype:ignore:end--> 4 5Good!
Output:
1<h1>Hello World</h1> 2 3<p>Good!</p>
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.
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3import rehypeSanitize from "rehype-sanitize"; 4 5export default function App() { 6 const [value, setValue] = React.useState(`**Hello world!!!** <IFRAME SRC=\"javascript:javascript:alert(window.origin);\"></IFRAME>`); 7 return ( 8 <div className="container"> 9 <MDEditor 10 value={value} 11 onChange={setValue} 12 previewOptions={{ 13 rehypePlugins: [[rehypeSanitize]], 14 }} 15 /> 16 </div> 17 ); 18}
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 code highlighting package, rehype-prism-plus
, highlightEnable
and showLineNumbers
functions will no longer work. (#586)highlight line
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor/nohighlight'; 3 4const code = `**Hello world!!!** 5\`\`\`js 6function demo() {} 7\`\`\` 8` 9 10export default function App() { 11 const [value, setValue] = React.useState(code); 12 return ( 13 <div className="container"> 14 <MDEditor 15 value={value} 16 onChange={setValue} 17 /> 18 <MDEditor.Markdown source={value} style={{ whiteSpace: 'pre-wrap' }} /> 19 </div> 20 ); 21}
"Below is an example that sets the placeholder
for the editor and defines the maximum input character length as 10
characters."
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3 4export default function App() { 5 const [value, setValue] = React.useState(""); 6 return ( 7 <MDEditor 8 value={value} 9 onChange={setValue} 10 textareaProps={{ 11 placeholder: 'Please enter Markdown text', 12 maxLength: 10 13 }} 14 /> 15 ); 16}
1import React, { useState } from "react"; 2import MDEditor, { commands } from '@uiw/react-md-editor'; 3 4const title3 = { 5 name: 'title3', 6 keyCommand: 'title3', 7 buttonProps: { 'aria-label': 'Insert title3' }, 8 icon: ( 9 <svg width="12" height="12" viewBox="0 0 520 520"> 10 <path fill="currentColor" d="M15.7083333,468 C7.03242448,468 0,462.030833 0,454.666667 L0,421.333333 C0,413.969167 7.03242448,408 15.7083333,408 L361.291667,408 C369.967576,408 377,413.969167 377,421.333333 L377,454.666667 C377,462.030833 369.967576,468 361.291667,468 L15.7083333,468 Z M21.6666667,366 C9.69989583,366 0,359.831861 0,352.222222 L0,317.777778 C0,310.168139 9.69989583,304 21.6666667,304 L498.333333,304 C510.300104,304 520,310.168139 520,317.777778 L520,352.222222 C520,359.831861 510.300104,366 498.333333,366 L21.6666667,366 Z M136.835938,64 L136.835937,126 L107.25,126 L107.25,251 L40.75,251 L40.75,126 L-5.68434189e-14,126 L-5.68434189e-14,64 L136.835938,64 Z M212,64 L212,251 L161.648438,251 L161.648438,64 L212,64 Z M378,64 L378,126 L343.25,126 L343.25,251 L281.75,251 L281.75,126 L238,126 L238,64 L378,64 Z M449.047619,189.550781 L520,189.550781 L520,251 L405,251 L405,64 L449.047619,64 L449.047619,189.550781 Z" /> 11 </svg> 12 ), 13 execute: (state, api) => { 14 let modifyText = `### ${state.selectedText}\n`; 15 if (!state.selectedText) { 16 modifyText = `### `; 17 } 18 api.replaceSelection(modifyText); 19 }, 20}; 21 22const title2 = { 23 name: 'title2', 24 keyCommand: 'title2', 25 render: (command, disabled, executeCommand) => { 26 return ( 27 <button 28 aria-label="Insert title2" 29 disabled={disabled} 30 onClick={(evn) => { 31 // evn.stopPropagation(); 32 executeCommand(command, command.groupName) 33 }} 34 > 35 <svg width="12" height="12" viewBox="0 0 520 520"> 36 <path fill="currentColor" d="M15.7083333,468 C7.03242448,468 0,462.030833 0,454.666667 L0,421.333333 C0,413.969167 7.03242448,408 15.7083333,408 L361.291667,408 C369.967576,408 377,413.969167 377,421.333333 L377,454.666667 C377,462.030833 369.967576,468 361.291667,468 L15.7083333,468 Z M21.6666667,366 C9.69989583,366 0,359.831861 0,352.222222 L0,317.777778 C0,310.168139 9.69989583,304 21.6666667,304 L498.333333,304 C510.300104,304 520,310.168139 520,317.777778 L520,352.222222 C520,359.831861 510.300104,366 498.333333,366 L21.6666667,366 Z M136.835938,64 L136.835937,126 L107.25,126 L107.25,251 L40.75,251 L40.75,126 L-5.68434189e-14,126 L-5.68434189e-14,64 L136.835938,64 Z M212,64 L212,251 L161.648438,251 L161.648438,64 L212,64 Z M378,64 L378,126 L343.25,126 L343.25,251 L281.75,251 L281.75,126 L238,126 L238,64 L378,64 Z M449.047619,189.550781 L520,189.550781 L520,251 L405,251 L405,64 L449.047619,64 L449.047619,189.550781 Z" /> 37 </svg> 38 </button> 39 ) 40 }, 41 execute: (state, api) => { 42 let modifyText = `## ${state.selectedText}\n`; 43 if (!state.selectedText) { 44 modifyText = `## `; 45 } 46 api.replaceSelection(modifyText); 47 }, 48} 49 50function SubChildren({ close, execute, getState, textApi, dispatch }) { 51 const [value, setValue] = useState('') 52 const insert = () => { 53 console.log('value:::', value) 54 textApi.replaceSelection(value) 55 } 56 return ( 57 <div style={{ width: 120, padding: 10 }}> 58 <div>My Custom Toolbar</div> 59 <input type="text" onChange={(e) => setValue(e.target.value)} /> 60 <button 61 type="button" 62 onClick={() => { 63 dispatch({ $value: '~~~~~~' }) 64 console.log('> execute: >>>>>', getState()) 65 }} 66 > 67 State 68 </button> 69 <button type="button" onClick={insert}>Insert</button> 70 <button type="button" onClick={() => close()}>Close</button> 71 <button type="button" onClick={() => execute()}>Execute</button> 72 </div> 73 ); 74} 75 76const subChild = { 77 name: 'update', 78 groupName: 'update', 79 icon: ( 80 <svg viewBox="0 0 1024 1024" width="12" height="12"> 81 <path fill="currentColor" d="M716.8 921.6a51.2 51.2 0 1 1 0 102.4H307.2a51.2 51.2 0 1 1 0-102.4h409.6zM475.8016 382.1568a51.2 51.2 0 0 1 72.3968 0l144.8448 144.8448a51.2 51.2 0 0 1-72.448 72.3968L563.2 541.952V768a51.2 51.2 0 0 1-45.2096 50.8416L512 819.2a51.2 51.2 0 0 1-51.2-51.2v-226.048l-57.3952 57.4464a51.2 51.2 0 0 1-67.584 4.2496l-4.864-4.2496a51.2 51.2 0 0 1 0-72.3968zM512 0c138.6496 0 253.4912 102.144 277.1456 236.288l10.752 0.3072C924.928 242.688 1024 348.0576 1024 476.5696 1024 608.9728 918.8352 716.8 788.48 716.8a51.2 51.2 0 1 1 0-102.4l8.3968-0.256C866.2016 609.6384 921.6 550.0416 921.6 476.5696c0-76.4416-59.904-137.8816-133.12-137.8816h-97.28v-51.2C691.2 184.9856 610.6624 102.4 512 102.4S332.8 184.9856 332.8 287.488v51.2H235.52c-73.216 0-133.12 61.44-133.12 137.8816C102.4 552.96 162.304 614.4 235.52 614.4l5.9904 0.3584A51.2 51.2 0 0 1 235.52 716.8C105.1648 716.8 0 608.9728 0 476.5696c0-132.1984 104.8064-239.872 234.8544-240.2816C258.5088 102.144 373.3504 0 512 0z" /> 82 </svg> 83 ), 84 children: (props) => <SubChildren {...props} />, 85 execute: (state, api) => { 86 console.log('>>>>>>update>>>>>', state) 87 }, 88 buttonProps: { 'aria-label': 'Insert title'} 89} 90 91export default function App() { 92 const [value, setValue] = React.useState("Hello Markdown! `Tab` key uses default behavior"); 93 return ( 94 <div className="container"> 95 <MDEditor 96 value={value} 97 onChange={setValue} 98 commands={[ 99 // Custom Toolbars 100 title3, title2, 101 commands.group([commands.title1, commands.title2, commands.title3, commands.title4, commands.title5, commands.title6], { 102 name: 'title', 103 groupName: 'title', 104 buttonProps: { 'aria-label': 'Insert title'} 105 }), 106 commands.divider, 107 commands.group([], subChild), 108 ]} 109 /> 110 </div> 111 ); 112}
Customize the toolbar with commands
and extraCommands
props.
1import React from "react"; 2import MDEditor, { commands } from '@uiw/react-md-editor'; 3 4export default function App() { 5 const [value, setValue] = React.useState("Hello Markdown! `Tab` key uses default behavior"); 6 return ( 7 <div className="container"> 8 <MDEditor 9 value={value} 10 onChange={setValue} 11 preview="edit" 12 commands={[ 13 commands.codeEdit, commands.codePreview 14 ]} 15 extraCommands={[ 16 commands.group([commands.title1, commands.title2, commands.title3, commands.title4, commands.title5, commands.title6], { 17 name: 'title', 18 groupName: 'title', 19 buttonProps: { 'aria-label': 'Insert title'} 20 }), 21 commands.divider, 22 commands.group([], { 23 name: 'update', 24 groupName: 'update', 25 icon: ( 26 <svg viewBox="0 0 1024 1024" width="12" height="12"> 27 <path fill="currentColor" d="M716.8 921.6a51.2 51.2 0 1 1 0 102.4H307.2a51.2 51.2 0 1 1 0-102.4h409.6zM475.8016 382.1568a51.2 51.2 0 0 1 72.3968 0l144.8448 144.8448a51.2 51.2 0 0 1-72.448 72.3968L563.2 541.952V768a51.2 51.2 0 0 1-45.2096 50.8416L512 819.2a51.2 51.2 0 0 1-51.2-51.2v-226.048l-57.3952 57.4464a51.2 51.2 0 0 1-67.584 4.2496l-4.864-4.2496a51.2 51.2 0 0 1 0-72.3968zM512 0c138.6496 0 253.4912 102.144 277.1456 236.288l10.752 0.3072C924.928 242.688 1024 348.0576 1024 476.5696 1024 608.9728 918.8352 716.8 788.48 716.8a51.2 51.2 0 1 1 0-102.4l8.3968-0.256C866.2016 609.6384 921.6 550.0416 921.6 476.5696c0-76.4416-59.904-137.8816-133.12-137.8816h-97.28v-51.2C691.2 184.9856 610.6624 102.4 512 102.4S332.8 184.9856 332.8 287.488v51.2H235.52c-73.216 0-133.12 61.44-133.12 137.8816C102.4 552.96 162.304 614.4 235.52 614.4l5.9904 0.3584A51.2 51.2 0 0 1 235.52 716.8C105.1648 716.8 0 608.9728 0 476.5696c0-132.1984 104.8064-239.872 234.8544-240.2816C258.5088 102.144 373.3504 0 512 0z" /> 28 </svg> 29 ), 30 children: ({ close, execute, getState, textApi }) => { 31 return ( 32 <div style={{ width: 120, padding: 10 }}> 33 <div>My Custom Toolbar</div> 34 <button type="button" onClick={() => console.log('> execute: >>>>>', getState())}>State</button> 35 <button type="button" onClick={() => close()}>Close</button> 36 <button type="button" onClick={() => execute()}>Execute</button> 37 </div> 38 ); 39 }, 40 execute: (state, api) => { 41 console.log('>>>>>>update>>>>>', state) 42 }, 43 buttonProps: { 'aria-label': 'Insert title'} 44 }), 45 commands.divider, commands.fullscreen 46 ]} 47 /> 48 </div> 49 ); 50}
re-render toolbar
element.
1import React from "react"; 2import MDEditor, { commands } from '@uiw/react-md-editor'; 3 4export default function App() { 5 const [value, setValue] = React.useState("Hello Markdown! `Tab` key uses default behavior"); 6 return ( 7 <div className="container"> 8 <MDEditor 9 value={value} 10 onChange={setValue} 11 preview="edit" 12 components={{ 13 toolbar: (command, disabled, executeCommand) => { 14 if (command.keyCommand === 'code') { 15 return ( 16 <button 17 aria-label="Insert code" 18 disabled={disabled} 19 onClick={(evn) => { 20 evn.stopPropagation(); 21 executeCommand(command, command.groupName) 22 }} 23 > 24 Code 25 </button> 26 ) 27 } 28 } 29 }} 30 /> 31 </div> 32 ); 33}
Custom Preview Command Tool
1import React, { useContext } from "react"; 2import MDEditor, { commands, EditorContext } from "@uiw/react-md-editor"; 3 4const Button = () => { 5 const { preview, dispatch } = useContext(EditorContext); 6 const click = () => { 7 dispatch({ 8 preview: preview === "edit" ? "preview" : "edit" 9 }); 10 }; 11 if (preview === "edit") { 12 return ( 13 <svg width="12" height="12" viewBox="0 0 520 520" onClick={click}> 14 <polygon 15 fill="currentColor" 16 points="0 71.293 0 122 319 122 319 397 0 397 0 449.707 372 449.413 372 71.293" 17 /> 18 <polygon 19 fill="currentColor" 20 points="429 71.293 520 71.293 520 122 481 123 481 396 520 396 520 449.707 429 449.413" 21 /> 22 </svg> 23 ); 24 } 25 return ( 26 <svg width="12" height="12" viewBox="0 0 520 520" onClick={click}> 27 <polygon 28 fill="currentColor" 29 points="0 71.293 0 122 38.023 123 38.023 398 0 397 0 449.707 91.023 450.413 91.023 72.293" 30 /> 31 <polygon 32 fill="currentColor" 33 points="148.023 72.293 520 71.293 520 122 200.023 124 200.023 397 520 396 520 449.707 148.023 450.413" 34 /> 35 </svg> 36 ); 37}; 38 39const codePreview = { 40 name: "preview", 41 keyCommand: "preview", 42 value: "preview", 43 icon: <Button /> 44}; 45 46const Disable = () => { 47 const { preview, dispatch } = useContext(EditorContext); 48 return ( 49 <button disabled={preview === "preview"}> 50 <svg viewBox="0 0 16 16" width="12px" height="12px"> 51 <path 52 d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8Zm.9 13H7v-1.8h1.9V13Zm-.1-3.6v.5H7.1v-.6c.2-2.1 2-1.9 1.9-3.2.1-.7-.3-1.1-1-1.1-.8 0-1.2.7-1.2 1.6H5c0-1.7 1.2-3 2.9-3 2.3 0 3 1.4 3 2.3.1 2.3-1.9 2-2.1 3.5Z" 53 fill="currentColor" 54 /> 55 </svg> 56 </button> 57 ) 58} 59 60const customButton = { 61 name: "disable", 62 keyCommand: "disable", 63 value: "disable", 64 icon: <Disable /> 65} 66 67export default function App() { 68 const [value, setValue] = React.useState("**Hello world!!!**"); 69 return ( 70 <div className="container"> 71 <div>The system automatically sets the theme</div> 72 <MDEditor 73 value={value} 74 preview="edit" 75 extraCommands={[codePreview, customButton, commands.fullscreen]} 76 onChange={(val) => setValue(val)} 77 /> 78 </div> 79 ); 80}
Add Help Command Tool
1import React, { useContext } from "react"; 2import MDEditor, { commands } from "@uiw/react-md-editor"; 3 4const help = { 5 name: "help", 6 keyCommand: "help", 7 buttonProps: { "aria-label": "Insert help" }, 8 icon: ( 9 <svg viewBox="0 0 16 16" width="12px" height="12px"> 10 <path 11 d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8Zm.9 13H7v-1.8h1.9V13Zm-.1-3.6v.5H7.1v-.6c.2-2.1 2-1.9 1.9-3.2.1-.7-.3-1.1-1-1.1-.8 0-1.2.7-1.2 1.6H5c0-1.7 1.2-3 2.9-3 2.3 0 3 1.4 3 2.3.1 2.3-1.9 2-2.1 3.5Z" 12 fill="currentColor" 13 /> 14 </svg> 15 ), 16 execute: (state, api) => { 17 window.open("https://www.markdownguide.org/basic-syntax/", "_blank"); 18 } 19}; 20 21export default function App() { 22 const [value, setValue] = React.useState("**Hello world!!!**"); 23 return ( 24 <MDEditor 25 value={value} 26 preview="edit" 27 commands={[...commands.getCommands(), help]} 28 onChange={(val) => setValue(val)} 29 /> 30 ); 31}
Internationalization Example, You can refer to commands-cn
for internationalization.
1import React, { useContext } from "react"; 2import MDEditor, { commands } from "@uiw/react-md-editor"; 3import { getCommands, getExtraCommands } from "@uiw/react-md-editor/commands-cn"; 4 5export default function App() { 6 const [value, setValue] = React.useState("**Hello world!!!**"); 7 return ( 8 <MDEditor 9 value={value} 10 preview="edit" 11 commands={[...getCommands()]} 12 extraCommands={[...getExtraCommands()]} 13 onChange={(val) => setValue(val)} 14 /> 15 ); 16}
1body .w-md-editor-text-pre > code, 2body .w-md-editor-text-input { 3 font-size: 23px !important; 4 line-height: 24px !important; 5}
The initial height can be adjusted through minHeight={100}
. Dragbar will automatically expire. You can hide the drag button through visibleDragbar={false}
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3 4export default function App() { 5 const [value, setValue] = React.useState("**Hello world!!!**"); 6 return ( 7 <div className="container"> 8 <MDEditor 9 value={value} 10 height="100%" 11 // minHeight={50} 12 visibleDragbar={false} 13 onChange={setValue} 14 /> 15 </div> 16 ); 17}
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3 4export default function App() { 5 const [value, setValue] = React.useState("**Hello world!!!** <style>body{display:none;}</style> "); 6 return ( 7 <div className="container"> 8 <MDEditor 9 value={value} 10 height="100%" 11 previewOptions={{ 12 disallowedElements: ['style'], 13 }} 14 visibleDragbar={false} 15 onChange={setValue} 16 /> 17 </div> 18 ); 19}
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3 4const textSample = `# Welcome to the Markdown Editor! 5 6This is a sample of the **React Markdown Editor**. 7 8## ✨ Features 9- Real-time preview 10- Custom styling support 11- Code highlighting 12- Auto focus at the end of the text 13 14## 📦 Sample Code 15 16\`\`\`javascript 17function hello() { 18 console.log("Hello, world!"); 19} 20\`\`\` 21 22## 🔗 Links 23 24Visit [uiwjs/react-md-editor](https://github.com/uiwjs/react-md-editor) for more information. 25`; 26 27export default function App() { 28 const [value, setValue] = React.useState(textSample); 29 return ( 30 <div className="container"> 31 <MDEditor 32 autoFocus={true} 33 value={value} 34 autoFocusEnd={true} 35 visibleDragbar={false} 36 onChange={setValue} 37 /> 38 </div> 39 ); 40}
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3 4export default function App() { 5 return ( 6 <div className="container"> 7 <MDEditor.Markdown source="Hello Markdown!" /> 8 </div> 9 ); 10}
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
1npm install katex
1import React from "react"; 2import MDEditor from '@uiw/react-md-editor'; 3import { getCodeString } from 'rehype-rewrite'; 4import katex from 'katex'; 5import 'katex/dist/katex.css'; 6 7const mdKaTeX = `This is to display the 8\`\$\$\c = \\pm\\sqrt{a^2 + b^2}\$\$\` 9 in one line 10 11\`\`\`KaTeX 12c = \\pm\\sqrt{a^2 + b^2} 13\`\`\` 14`; 15 16export default function App() { 17 const [value, setValue] = React.useState(mdKaTeX); 18 return ( 19 <MDEditor 20 value={value} 21 onChange={(val) => setValue(val)} 22 previewOptions={{ 23 components: { 24 code: ({ children = [], className, ...props }) => { 25 if (typeof children === 'string' && /^\$\$(.*)\$\$/.test(children)) { 26 const html = katex.renderToString(children.replace(/^\$\$(.*)\$\$/, '$1'), { 27 throwOnError: false, 28 }); 29 return <code dangerouslySetInnerHTML={{ __html: html }} style={{ background: 'transparent' }} />; 30 } 31 const code = props.node && props.node.children ? getCodeString(props.node.children) : children; 32 if ( 33 typeof code === 'string' && 34 typeof className === 'string' && 35 /^language-katex/.test(className.toLocaleLowerCase()) 36 ) { 37 const html = katex.renderToString(code, { 38 throwOnError: false, 39 }); 40 return <code style={{ fontSize: '150%' }} dangerouslySetInnerHTML={{ __html: html }} />; 41 } 42 return <code className={String(className)}>{children}</code>; 43 }, 44 }, 45 }} 46 /> 47 ); 48}
1import React, { useState } from "react"; 2import MDEditor, { commands, ICommand, TextState, TextAreaTextApi } from "@uiw/react-md-editor"; 3import domToImage from "dom-to-image"; 4 5const textToImage: ICommand = { 6 name: "Text To Image", 7 keyCommand: "text2image", 8 buttonProps: { "aria-label": "Insert title3" }, 9 icon: ( 10 <svg width="12" height="12" viewBox="0 0 20 20"> 11 <path fill="currentColor" d="M15 9c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm4-7H1c-.55 0-1 .45-1 1v14c0 .55.45 1 1 1h18c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm-1 13l-6-5-2 2-4-5-4 8V4h16v11z" ></path> 12 </svg> 13 ), 14 execute: (state: TextState, api: TextAreaTextApi) => { 15 const dom = document.getElementsByClassName("gooooooooo")[0]; 16 if (dom) { 17 domToImage.toJpeg(dom, {}).then((dataUrl) => { 18 const link = document.createElement("a"); 19 link.download = "image.jpg"; 20 link.href = dataUrl; 21 link.click(); 22 }); 23 } 24 } 25}; 26 27export default function App() { 28 const [value, setValue] = useState('**Hello world!!!**'); 29 console.log('value::', value) 30 return ( 31 <div className="container"> 32 <MDEditor 33 className="gooooooooo" 34 onChange={(newValue = "") => setValue(newValue)} 35 value={value} 36 commands={[ 37 textToImage, 38 commands.divider 39 ]} 40 /> 41 </div> 42 ); 43}
Using mermaid to generation of diagram and flowchart from text in a similar manner as markdown
1npm install mermaid
1import React, { useState, useRef, useEffect, Fragment, useCallback } from "react"; 2import MDEditor from "@uiw/react-md-editor"; 3import { getCodeString } from 'rehype-rewrite'; 4import mermaid from "mermaid"; 5 6const 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. 7 8\`\`\`mermaid 9graph TD 10A[Hard] -->|Text| B(Round) 11B --> C{Decision} 12C -->|One| D[Result 1] 13C -->|Two| E[Result 2] 14\`\`\` 15 16\`\`\`mermaid 17sequenceDiagram 18Alice->>John: Hello John, how are you? 19loop Healthcheck 20 John->>John: Fight against hypochondria 21end 22Note right of John: Rational thoughts! 23John-->>Alice: Great! 24John->>Bob: How about you? 25Bob-->>John: Jolly good! 26\`\`\` 27`; 28 29const randomid = () => parseInt(String(Math.random() * 1e15), 10).toString(36); 30const Code = ({ inline, children = [], className, ...props }) => { 31 const demoid = useRef(`dome${randomid()}`); 32 const [container, setContainer] = useState(null); 33 const isMermaid = 34 className && /^language-mermaid/.test(className.toLocaleLowerCase()); 35 const code = children 36 ? getCodeString(props.node.children) 37 : children[0] || ""; 38 39 useEffect(() => { 40 if (container && isMermaid && demoid.current && code) { 41 mermaid 42 .render(demoid.current, code) 43 .then(({ svg, bindFunctions }) => { 44 container.innerHTML = svg; 45 if (bindFunctions) { 46 bindFunctions(container); 47 } 48 }) 49 .catch((error) => { 50 console.log("error:", error); 51 }); 52 } 53 }, [container, isMermaid, code, demoid]); 54 55 const refElement = useCallback((node) => { 56 if (node !== null) { 57 setContainer(node); 58 } 59 }, []); 60 61 if (isMermaid) { 62 return ( 63 <Fragment> 64 <code id={demoid.current} style={{ display: "none" }} /> 65 <code className={className} ref={refElement} data-name="mermaid" /> 66 </Fragment> 67 ); 68 } 69 return <code className={className}>{children}</code>; 70}; 71 72export default function App() { 73 const [value, setValue] = useState(mdMermaid); 74 return ( 75 <MDEditor 76 onChange={(newValue = "") => setValue(newValue)} 77 textareaProps={{ 78 placeholder: "Please enter Markdown text" 79 }} 80 height={500} 81 value={value} 82 previewOptions={{ 83 components: { 84 code: Code 85 } 86 }} 87 /> 88 ); 89}
Use examples in nextjs. #52
#224
1npm install next-remove-imports 2npm install @uiw/react-md-editor@v3.6.0
1// next.config.js 2const removeImports = require('next-remove-imports')(); 3module.exports = removeImports({});
1import "@uiw/react-md-editor/markdown-editor.css"; 2import "@uiw/react-markdown-preview/markdown.css"; 3import dynamic from "next/dynamic"; 4import { useState } from "react"; 5 6import * as commands from "@uiw/react-md-editor/commands" 7 8const MDEditor = dynamic( 9 () => import("@uiw/react-md-editor"), 10 { ssr: false } 11); 12 13function HomePage() { 14 const [value, setValue] = useState("**Hello world!!!**"); 15 return ( 16 <div> 17 <MDEditor value={value} onChange={setValue} /> 18 </div> 19 ); 20} 21 22export 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.
1<html data-color-mode="dark">
1document.documentElement.setAttribute('data-color-mode', 'dark')
2document.documentElement.setAttribute('data-color-mode', 'light')
Inherit custom color variables by adding .wmde-markdown-var
selector. Setting theme styles with data-color-mode="light"
.
1<div data-color-mode="light"> 2 <div className="wmde-markdown-var"> </div> 3 <MDEditor source="Hello World!" /> 4</div>
value: string
: The Markdown value.onChange?: (value?: string, event?: React.ChangeEvent<HTMLTextAreaElement>, state?: ContextStore)
: 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. #419
textarea
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$ npm install # Installation dependencies 2$ npm run build # Compile all package
@uiw/react-md-editor
package:1$ cd core 2# listen to the component compile and output the .js file 3# listen for compilation output type .d.ts file 4$ npm run watch # Monitor the compiled package `@uiw/react-md-editor`
1npm run start
As always, thanks to our amazing contributors!
Made with contributors.
Licensed under the MIT License.
No vulnerabilities found.
Reason
12 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
Reason
packaging workflow detected
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
Found 5/30 approved changesets -- score normalized to 1
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
Reason
security policy file not detected
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-06-30
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More