react-md-editor
A simple markdown editor with preview, implemented with React.js and TypeScript.
Top Related Projects
Potato
Markdown component for React
React Ace Component
🍞📝 Markdown WYSIWYG Editor. GFM Standard + Chart & UML Extensible.
Quick Overview
React-md-editor is a lightweight Markdown editor component for React applications. It provides a user-friendly interface for writing and editing Markdown content, with features like live preview, syntax highlighting, and customizable toolbar options.
Pros
- Easy integration with React projects
- Supports both controlled and uncontrolled component modes
- Customizable toolbar with various Markdown formatting options
- Live preview of rendered Markdown content
Cons
- Limited advanced Markdown features compared to some other editors
- May require additional configuration for complex use cases
- Documentation could be more comprehensive
- Some reported issues with TypeScript type definitions
Code Examples
- Basic usage:
import React from 'react';
import MDEditor from '@uiw/react-md-editor';
function App() {
const [value, setValue] = React.useState("**Hello world!!!**");
return (
<div className="container">
<MDEditor
value={value}
onChange={setValue}
/>
</div>
);
}
- Using preview mode:
import React from 'react';
import MDEditor from '@uiw/react-md-editor';
function App() {
return (
<div className="container">
<MDEditor.Markdown source="# Markdown Preview" />
</div>
);
}
- Customizing toolbar options:
import React from 'react';
import MDEditor from '@uiw/react-md-editor';
function App() {
return (
<div className="container">
<MDEditor
value="# Custom Toolbar"
commands={[
'bold', 'italic', 'strikethrough',
'divider',
'link', 'image', 'code'
]}
/>
</div>
);
}
Getting Started
To use react-md-editor in your React project, follow these steps:
- Install the package:
npm install @uiw/react-md-editor
- Import and use the component in your React application:
import React from 'react';
import MDEditor from '@uiw/react-md-editor';
function MyComponent() {
const [value, setValue] = React.useState("# Hello, Markdown!");
return <MDEditor value={value} onChange={setValue} />;
}
export default MyComponent;
This will create a basic Markdown editor with default settings. You can customize the editor by passing additional props as needed.
Competitor Comparisons
Potato
Pros of potato
- Simpler and more lightweight, focusing on basic Markdown editing functionality
- Potentially easier to integrate into existing projects due to its minimalistic approach
- May have a smaller learning curve for developers new to Markdown editors
Cons of potato
- Less feature-rich compared to react-md-editor, which offers more advanced editing capabilities
- Limited customization options and theming support
- Smaller community and fewer updates, potentially leading to slower bug fixes and feature additions
Code Comparison
react-md-editor:
import MDEditor from '@uiw/react-md-editor';
export default function App() {
const [value, setValue] = React.useState("**Hello world!!!**");
return <MDEditor value={value} onChange={setValue} />;
}
potato:
import { Editor } from 'potato';
function App() {
return <Editor />;
}
The code comparison shows that react-md-editor offers more out-of-the-box functionality with state management and onChange handlers, while potato provides a simpler implementation. This reflects the overall difference in complexity and feature set between the two libraries.
Markdown component for React
Pros of react-markdown
- More flexible and customizable, allowing for greater control over rendering
- Supports a wider range of Markdown syntax and extensions
- Larger community and more frequent updates
Cons of react-markdown
- Requires more setup and configuration for advanced features
- May have a steeper learning curve for beginners
- Lacks built-in editor functionality, focusing solely on rendering
Code Comparison
react-markdown:
import ReactMarkdown from 'react-markdown'
const MarkdownRenderer = ({ content }) => (
<ReactMarkdown>{content}</ReactMarkdown>
)
react-md-editor:
import MDEditor from '@uiw/react-md-editor'
const MarkdownEditor = () => (
<MDEditor value={content} onChange={setContent} />
)
The code comparison highlights the key difference between the two libraries. react-markdown is primarily a renderer, while react-md-editor provides an all-in-one editor and preview solution. react-markdown offers more flexibility in how Markdown is rendered, but requires additional setup for editing functionality. react-md-editor, on the other hand, provides a ready-to-use editor component with built-in preview capabilities, making it easier to implement a full Markdown editing experience with less code.
React Ace Component
Pros of react-ace
- More versatile, supporting multiple programming languages beyond Markdown
- Offers advanced code editing features like syntax highlighting and autocompletion
- Highly customizable with numerous configuration options
Cons of react-ace
- Larger bundle size due to its comprehensive feature set
- Steeper learning curve for basic implementation and customization
- May be overkill for simple Markdown editing tasks
Code Comparison
react-ace:
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/theme-github';
<AceEditor
mode="javascript"
theme="github"
onChange={onChange}
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
/>
react-md-editor:
import MDEditor from '@uiw/react-md-editor';
<MDEditor
value={value}
onChange={onChange}
/>
react-ace provides a more feature-rich editing experience suitable for various programming languages, while react-md-editor offers a simpler, more focused solution for Markdown editing. The choice between the two depends on the specific requirements of your project, such as the need for multi-language support or the desire for a lightweight Markdown-specific editor.
🍞📝 Markdown WYSIWYG Editor. GFM Standard + Chart & UML Extensible.
Pros of tui.editor
- More comprehensive feature set, including WYSIWYG editing and various plugins
- Better support for tables and complex formatting
- Extensive documentation and examples
Cons of tui.editor
- Larger bundle size due to more features
- Steeper learning curve for implementation and customization
- Less focused on React integration compared to react-md-editor
Code Comparison
react-md-editor:
import MDEditor from '@uiw/react-md-editor';
export default function App() {
return <MDEditor value="# Hello, world!" />;
}
tui.editor:
import '@toast-ui/editor/dist/toastui-editor.css';
import { Editor } from '@toast-ui/react-editor';
export default function App() {
return <Editor initialValue="# Hello, world!" />;
}
Summary
tui.editor offers a more feature-rich editing experience with WYSIWYG capabilities and extensive plugins, making it suitable for complex editing needs. However, it comes with a larger bundle size and potentially more complex implementation. react-md-editor, on the other hand, provides a simpler, more lightweight solution specifically tailored for React applications, focusing primarily on Markdown editing. The choice between the two depends on the specific requirements of your project, such as the need for WYSIWYG editing, table support, or a more streamlined Markdown-only experience.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
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.
Features
- ð Indent line or selected text by pressing tab key, with customizable indentation.
- â»ï¸ Based on
textareaencapsulation, does not depend on any modern code editors. - ð§ Does not depend on the
uiwcomponent 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+.
Quick Start
npm i @uiw/react-md-editor
or
yarn add @uiw/react-md-editor
Using
import React from "react";
import MDEditor from '@uiw/react-md-editor';
export default function App() {
const [value, setValue] = React.useState("**Hello world!!!**");
return (
<div className="container">
<MDEditor
value={value}
onChange={setValue}
/>
<MDEditor.Markdown source={value} style={{ whiteSpace: 'pre-wrap' }} />
</div>
);
}
Special Markdown syntax
Supports for CSS Style
Use HTML comments <!--rehype:xxx--> to let Markdown support style customization.
## Title
<!--rehype:style=display: flex; height: 230px; align-items: center; justify-content: center; font-size: 38px;-->
Markdown Supports **Style**<!--rehype:style=color: red;-->
Ignore content display via HTML comments
Shown in GitHub readme, excluded in HTML.
# Hello World
<!--rehype:ignore:start-->Hello World<!--rehype:ignore:end-->
Good!
Output:
<h1>Hello World</h1>
<p>Good!</p>
Security
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.
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!!!** <IFRAME SRC=\"javascript:javascript:alert(window.origin);\"></IFRAME>`);
return (
<div className="container">
<MDEditor
value={value}
onChange={setValue}
previewOptions={{
rehypePlugins: [[rehypeSanitize]],
}}
/>
</div>
);
}
Remove Code Highlight
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
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 (
<div className="container">
<MDEditor
value={value}
onChange={setValue}
/>
<MDEditor.Markdown source={value} style={{ whiteSpace: 'pre-wrap' }} />
</div>
);
}
Placeholder & maxLength
"Below is an example that sets the placeholder for the editor and defines the maximum input character length as 10 characters."
import React from "react";
import MDEditor from '@uiw/react-md-editor';
export default function App() {
const [value, setValue] = React.useState("");
return (
<MDEditor
value={value}
onChange={setValue}
textareaProps={{
placeholder: 'Please enter Markdown text',
maxLength: 10
}}
/>
);
}
Custom Toolbars
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: (
<svg width="12" height="12" viewBox="0 0 520 520">
<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" />
</svg>
),
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 (
<button
aria-label="Insert title2"
disabled={disabled}
onClick={(evn) => {
// evn.stopPropagation();
executeCommand(command, command.groupName)
}}
>
<svg width="12" height="12" viewBox="0 0 520 520">
<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" />
</svg>
</button>
)
},
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 (
<div style={{ width: 120, padding: 10 }}>
<div>My Custom Toolbar</div>
<input type="text" onChange={(e) => setValue(e.target.value)} />
<button
type="button"
onClick={() => {
dispatch({ $value: '~~~~~~' })
console.log('> execute: >>>>>', getState())
}}
>
State
</button>
<button type="button" onClick={insert}>Insert</button>
<button type="button" onClick={() => close()}>Close</button>
<button type="button" onClick={() => execute()}>Execute</button>
</div>
);
}
const subChild = {
name: 'update',
groupName: 'update',
icon: (
<svg viewBox="0 0 1024 1024" width="12" height="12">
<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" />
</svg>
),
children: (props) => <SubChildren {...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 (
<div className="container">
<MDEditor
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),
]}
/>
</div>
);
}
Customize the toolbar with commands and extraCommands props.
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 (
<div className="container">
<MDEditor
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: (
<svg viewBox="0 0 1024 1024" width="12" height="12">
<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" />
</svg>
),
children: ({ close, execute, getState, textApi }) => {
return (
<div style={{ width: 120, padding: 10 }}>
<div>My Custom Toolbar</div>
<button type="button" onClick={() => console.log('> execute: >>>>>', getState())}>State</button>
<button type="button" onClick={() => close()}>Close</button>
<button type="button" onClick={() => execute()}>Execute</button>
</div>
);
},
execute: (state, api) => {
console.log('>>>>>>update>>>>>', state)
},
buttonProps: { 'aria-label': 'Insert title'}
}),
commands.divider, commands.fullscreen
]}
/>
</div>
);
}
re-render toolbar element.
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 (
<div className="container">
<MDEditor
value={value}
onChange={setValue}
preview="edit"
components={{
toolbar: (command, disabled, executeCommand) => {
if (command.keyCommand === 'code') {
return (
<button
aria-label="Insert code"
disabled={disabled}
onClick={(evn) => {
evn.stopPropagation();
executeCommand(command, command.groupName)
}}
>
Code
</button>
)
}
}
}}
/>
</div>
);
}
Custom Preview Command Tool
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 (
<svg width="12" height="12" viewBox="0 0 520 520" onClick={click}>
<polygon
fill="currentColor"
points="0 71.293 0 122 319 122 319 397 0 397 0 449.707 372 449.413 372 71.293"
/>
<polygon
fill="currentColor"
points="429 71.293 520 71.293 520 122 481 123 481 396 520 396 520 449.707 429 449.413"
/>
</svg>
);
}
return (
<svg width="12" height="12" viewBox="0 0 520 520" onClick={click}>
<polygon
fill="currentColor"
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"
/>
<polygon
fill="currentColor"
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"
/>
</svg>
);
};
const codePreview = {
name: "preview",
keyCommand: "preview",
value: "preview",
icon: <Button />
};
const Disable = () => {
const { preview, dispatch } = useContext(EditorContext);
return (
<button disabled={preview === "preview"}>
<svg viewBox="0 0 16 16" width="12px" height="12px">
<path
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"
fill="currentColor"
/>
</svg>
</button>
)
}
const customButton = {
name: "disable",
keyCommand: "disable",
value: "disable",
icon: <Disable />
}
export default function App() {
const [value, setValue] = React.useState("**Hello world!!!**");
return (
<div className="container">
<div>The system automatically sets the theme</div>
<MDEditor
value={value}
preview="edit"
extraCommands={[codePreview, customButton, commands.fullscreen]}
onChange={(val) => setValue(val)}
/>
</div>
);
}
Add Help Command Tool
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: (
<svg viewBox="0 0 16 16" width="12px" height="12px">
<path
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"
fill="currentColor"
/>
</svg>
),
execute: (state, api) => {
window.open("https://www.markdownguide.org/basic-syntax/", "_blank");
}
};
export default function App() {
const [value, setValue] = React.useState("**Hello world!!!**");
return (
<MDEditor
value={value}
preview="edit"
commands={[...commands.getCommands(), help]}
onChange={(val) => setValue(val)}
/>
);
}
Internationalization Example, You can refer to commands-cn for internationalization.
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 (
<MDEditor
value={value}
preview="edit"
commands={[...getCommands()]}
extraCommands={[...getExtraCommands()]}
onChange={(val) => setValue(val)}
/>
);
}
Editor Font Size
body .w-md-editor-text-pre > code,
body .w-md-editor-text-input {
font-size: 23px !important;
line-height: 24px !important;
}
Editor height adapts to text
The initial height can be adjusted through minHeight={100}. Dragbar will automatically expire. You can hide the drag button through visibleDragbar={false}
import React from "react";
import MDEditor from '@uiw/react-md-editor';
export default function App() {
const [value, setValue] = React.useState("**Hello world!!!**");
return (
<div className="container">
<MDEditor
value={value}
height="100%"
// minHeight={50}
visibleDragbar={false}
onChange={setValue}
/>
</div>
);
}
Disallowed Elements
import React from "react";
import MDEditor from '@uiw/react-md-editor';
export default function App() {
const [value, setValue] = React.useState("**Hello world!!!** <style>body{display:none;}</style> ");
return (
<div className="container">
<MDEditor
value={value}
height="100%"
previewOptions={{
disallowedElements: ['style'],
}}
visibleDragbar={false}
onChange={setValue}
/>
</div>
);
}
Place the cursor at the end of the text
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**.
## ⨠Features
- Real-time preview
- Custom styling support
- Code highlighting
- Auto focus at the end of the text
## ð¦ Sample Code
\`\`\`javascript
function hello() {
console.log("Hello, world!");
}
\`\`\`
## ð Links
Visit [uiwjs/react-md-editor](https://github.com/uiwjs/react-md-editor) for more information.
`;
export default function App() {
const [value, setValue] = React.useState(textSample);
return (
<div className="container">
<MDEditor
autoFocus={true}
value={value}
autoFocusEnd={true}
visibleDragbar={false}
onChange={setValue}
/>
</div>
);
}
Preview Markdown
import React from "react";
import MDEditor from '@uiw/react-md-editor';
export default function App() {
return (
<div className="container">
<MDEditor.Markdown source="Hello Markdown!" />
</div>
);
}
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.
The following example is preview in CodeSandbox.
â ï¸ Upgrade v2 to v3 d025430
npm install katex
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}\$\$\`
in one line
\`\`\`KaTeX
c = \\pm\\sqrt{a^2 + b^2}
\`\`\`
`;
export default function App() {
const [value, setValue] = React.useState(mdKaTeX);
return (
<MDEditor
value={value}
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 <code dangerouslySetInnerHTML={{ __html: html }} style={{ background: 'transparent' }} />;
}
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 <code style={{ fontSize: '150%' }} dangerouslySetInnerHTML={{ __html: html }} />;
}
return <code className={String(className)}>{children}</code>;
},
},
}}
/>
);
}
Markdown text to Image
import React, { useState } from "react";
import MDEditor, { commands, 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: (
<svg width="12" height="12" viewBox="0 0 20 20">
<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>
</svg>
),
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 (
<div className="container">
<MDEditor
className="gooooooooo"
onChange={(newValue = "") => setValue(newValue)}
value={value}
commands={[
textToImage,
commands.divider
]}
/>
</div>
);
}
Support Custom Mermaid Preview
Using mermaid to generation of diagram and flowchart from text in a similar manner as markdown
npm install mermaid
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 (
<Fragment>
<code id={demoid.current} style={{ display: "none" }} />
<code className={className} ref={refElement} data-name="mermaid" />
</Fragment>
);
}
return <code className={className}>{children}</code>;
};
export default function App() {
const [value, setValue] = useState(mdMermaid);
return (
<MDEditor
onChange={(newValue = "") => setValue(newValue)}
textareaProps={{
placeholder: "Please enter Markdown text"
}}
height={500}
value={value}
previewOptions={{
components: {
code: Code
}
}}
/>
);
}
Support Nextjs
Use examples in nextjs. #52 #224
npm install next-remove-imports
npm install @uiw/react-md-editor@v3.6.0
// next.config.js
const removeImports = require('next-remove-imports')();
module.exports = removeImports({});
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 (
<div>
<MDEditor value={value} onChange={setValue} />
</div>
);
}
export default HomePage;
Support dark-mode/night-mode
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 data-color-mode="dark">
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".
<div data-color-mode="light">
<div className="wmde-markdown-var"> </div>
<MDEditor source="Hello World!" />
</div>
Props
value: string: The Markdown value.onChange?: (value?: string, event?: React.ChangeEvent<HTMLTextAreaElement>, state?: ContextStore): Event handler for theonChangeevent.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 ofICommand, which, each one, contain acommandsproperty. 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 makeMarkdown Editorfocus itself on initialization.autoFocusEnd?: false: Can be used to makeMarkdown Editorfocus on the end of text on initialization.previewOptions?: ReactMarkdown.ReactMarkdownProps: This is reset @uiw/react-markdown-preview settings.textareaProps?: TextareaHTMLAttributes: Set thetextarearelated props.:renderTextarea?: (props, opts) => JSX.Element;@deprecatedPlease use->renderTextareacomponents. Use div to replace TextArea or re-render TextArea. #193components: re-render textarea/toolbar element. #419textareaUse div to replace TextArea or re-render TextAreatoolbarOverride the default command element.toolbar<command[].renderpreviewCustom markdown preview. #429
height?: number=200: The height of the editor. ï¸â ï¸Dragbaris invalid whenheightparameter 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 isfalse, which increases the editing speed.fullscreen?: boolean=false: Show markdown preview.overflow?: boolean=true: Disablefullscreensetting body stylespreview?: 'live' | 'edit' | 'preview': Default valuelive, Show markdown preview.maxHeight?: number=1200: Maximum drag height. ThevisibleDragbar=truevalue is valid.minHeight?: number=100: Minimum drag height. ThevisibleDragbar=truevalue is valid.tabSize?: number=2: The number of characters to insert when pressing tab key. Default2spaces.defaultTabEnable?: boolean=false: Iffalse, thetabkey inserts a tab character into the textarea. Iftrue, thetabkey 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.
Development
- Install dependencies
$ npm install # Installation dependencies
$ npm run build # Compile all package
- Development
@uiw/react-md-editorpackage:
$ cd core
# listen to the component compile and output the .js file
# listen for compilation output type .d.ts file
$ npm run watch # Monitor the compiled package `@uiw/react-md-editor`
- Launch documentation site
npm run start
Related
- @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.
Contributors
As always, thanks to our amazing contributors!
Made with contributors.
License
Licensed under the MIT License.
Top Related Projects
Potato
Markdown component for React
React Ace Component
🍞📝 Markdown WYSIWYG Editor. GFM Standard + Chart & UML Extensible.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot