This post is for testing in production Loremipsumdolorsitametloremipsumdolorsitamet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At elementum eu facilisis sed. Sagittis orci a scelerisque purus semper eget duis at tellus. Ornare massa eget egestas purus viverra accumsan in nisl nisi. Purus sit amet luctus venenatis lectus magna. Posuere ac ut consequat semper viverra nam libero justo. Facilisi cras fermentum odio eu. Morbi quis commodo odio aenean sed. Orci sagittis eu volutpat odio facilisis mauris sit. Tellus integer feugiat scelerisque varius morbi enim.
Long text trying to make a horizontal scroll 112321312dfdafdsajdfdafdsafadfafdfljslafjlkdfadsf4456LoremipsumdolorsitametLoremipsumdolorsitamet112321312dfdafdsajdfdafdsafadfafdfljslafjlkdfadsf4456LoremipsumdolorsitametLoremipsumdolorsitamet
112321312dfdafdsajdfdafdsafadfafdfljslafjlkdfadsf4456LoremipsumdolorsitametLoremipsumdolorsitamet112321312dfdafdsajdfdafdsafadfafdfljslafjlkdfadsf4456LoremipsumdolorsitametLoremipsumdolorsitamet
Heading2
Heading3
- 📑 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At elementum eu facilisis sed.
- ♻️ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At elementum eu facilisis sed.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At elementum eu facilisis sed.
- 🚧 Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet - 🚘 Bold text link to Next.js
Quick Start
npm i awesome-package
Using image as a link
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-->
<!--rehype:style=color: red; font-weight: bold;--> 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>
);
}
Custom Toolbars
import React 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);
},
}
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([], {
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'}
}),
]}
/>
</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 } from "react";
import ReactDOM from "react-dom";
import MDEditor from "@uiw/react-md-editor";
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 code = getCode(children);
const demo = useRef(null);
useEffect(() => {
if (demo.current) {
try {
const str = mermaid.render(demoid.current, code, () => null, demo.current);
// @ts-ignore
demo.current.innerHTML = str;
} catch (error) {
// @ts-ignore
demo.current.innerHTML = error;
}
}
}, [code, demo]);
if (
typeof code === "string" && typeof className === "string" &&
/^language-mermaid/.test(className.toLocaleLowerCase())
) {
return (
<code ref={demo}>
<code id={demoid.current} style={{ display: "none" }} />
</code>
);
}
return <code className={String(className)}>{children}</code>;
};
const getCode = (arr = []) => arr.map((dt) => {
if (typeof dt === "string") {
return dt;
}
if (dt.props && dt.props.children) {
return getCode(dt.props.children);
}
return false;
}).filter(Boolean).join("");
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
Sequence of image as a link without a new line
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. #429
height?: number=200
: The height of the editor. ️⚠️Dragbar
is invalid whenheight
parameter percentage.
Html tag
<a href="https://github.com/uiwjs/react-md-editor/graphs/contributors"> <img src="https://uiwjs.github.io/react-md-editor/CONTRIBUTORS.svg" /> </a> <p>Some paragraph text</p>Some footer text