Serializing Markdown
Markdown <-> Slate
components/demo.tsx
'use client';
import React from 'react';
import { Plate } from '@udecode/plate-common/react';
import { editorPlugins } from '@/components/editor/plugins/editor-plugins';
import { useCreateEditor } from '@/components/editor/use-create-editor';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';
import { DEMO_VALUES } from './values/demo-values';
export default function Demo({ id }: { id: string }) {
const editor = useCreateEditor({
plugins: [...editorPlugins],
value: DEMO_VALUES[id],
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}
Installation
npm install @udecode/plate-markdown
Usage
Markdown to Slate
import { MarkdownPlugin } from '@udecode/plate-markdown';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
MarkdownPlugin,
],
});
const value = editor.api.markdown.deserialize('**Hello world!**');
components/markdown-to-slate-demo.tsx
'use client';
import React, { useState } from 'react';
import { withProps } from '@udecode/cn';
import {
BoldPlugin,
CodePlugin,
ItalicPlugin,
StrikethroughPlugin,
SubscriptPlugin,
SuperscriptPlugin,
UnderlinePlugin,
} from '@udecode/plate-basic-marks/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
import {
CodeBlockPlugin,
CodeLinePlugin,
CodeSyntaxPlugin,
} from '@udecode/plate-code-block/react';
import { type Value, replaceNodeChildren } from '@udecode/plate-common';
import {
type PlateEditor,
ParagraphPlugin,
Plate,
PlateLeaf,
usePlateEditor,
} from '@udecode/plate-common/react';
import { HEADING_KEYS } from '@udecode/plate-heading';
import { HighlightPlugin } from '@udecode/plate-highlight/react';
import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';
import { KbdPlugin } from '@udecode/plate-kbd/react';
import { LinkPlugin } from '@udecode/plate-link/react';
import { MarkdownPlugin, deserializeMd } from '@udecode/plate-markdown';
import { InlineEquationPlugin } from '@udecode/plate-math/react';
import { ImagePlugin } from '@udecode/plate-media/react';
import {
TableCellHeaderPlugin,
TableCellPlugin,
TablePlugin,
TableRowPlugin,
} from '@udecode/plate-table/react';
import { cloneDeep } from 'lodash';
import remarkEmoji from 'remark-emoji';
import { autoformatPlugin } from '@/components/editor/plugins/autoformat-plugin';
import { basicNodesPlugins } from '@/components/editor/plugins/basic-nodes-plugins';
import { indentListPlugins } from '@/components/editor/plugins/indent-list-plugins';
import { linkPlugin } from '@/components/editor/plugins/link-plugin';
import { mediaPlugins } from '@/components/editor/plugins/media-plugins';
import { tablePlugin } from '@/components/editor/plugins/table-plugin';
import { BlockquoteElement } from '@/components/plate-ui/blockquote-element';
import { CodeBlockElement } from '@/components/plate-ui/code-block-element';
import { CodeLeaf } from '@/components/plate-ui/code-leaf';
import { CodeLineElement } from '@/components/plate-ui/code-line-element';
import { CodeSyntaxLeaf } from '@/components/plate-ui/code-syntax-leaf';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';
import { HeadingElement } from '@/components/plate-ui/heading-element';
import { HighlightLeaf } from '@/components/plate-ui/highlight-leaf';
import { HrElement } from '@/components/plate-ui/hr-element';
import { ImageElement } from '@/components/plate-ui/image-element';
import { KbdLeaf } from '@/components/plate-ui/kbd-leaf';
import { LinkElement } from '@/components/plate-ui/link-element';
import { ParagraphElement } from '@/components/plate-ui/paragraph-element';
import {
TableCellElement,
TableCellHeaderElement,
} from '@/components/plate-ui/table-cell-element';
import { TableElement } from '@/components/plate-ui/table-element';
import { TableRowElement } from '@/components/plate-ui/table-row-element';
const initialMarkdown = `# Markdown syntax guide
## Headers
# This is a Heading h1
## This is a Heading h2
###### This is a Heading h6
## Emphasis
*This text will be italic*
_This will also be italic_
**This text will be bold**
__This will also be bold__
_You **can** combine them_
## Lists
### Unordered
* Item 1
* Item 2
* Item 2a
* Item 2b
### Ordered
1. Item 1
2. Item 2
3. Item 3
1. Item 3a
2. Item 3b
## Images
![This is an alt text.](https://images.unsplash.com/photo-1506619216599-9d16d0903dfd?q=80&w=2669&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D "This is a sample image.")
## Links
You may be using [Markdown Live Preview](https://markdownlivepreview.com/).
## Blockquotes
> Markdown is a lightweight markup language with plain-text-formatting syntax, created in 2004 by John Gruber with Aaron Swartz.
## Tables
| Left columns | Right columns |
| ------------- |:-------------:|
| left foo | right foo |
| left bar | right bar |
| left baz | right baz |
## Blocks of code
\`\`\`js
let message = 'Hello world';
alert(message);
\`\`\`
## Inline code
This web site is using \`plate\`.
## GitHub Flavored Markdown
### Task Lists
- [x] Completed task
- [ ] Incomplete task
- [x] @mentions, #refs, [links](), **formatting**, and <del>tags</del> supported
- [ ] list syntax required (any unordered or ordered list supported)
### Strikethrough
~~This text is strikethrough~~
### Autolinks
Visit https://github.com automatically converts to a link
Email example@example.com also converts automatically
### Emoji
:smile: :heart:
`;
const markdownPlugin = MarkdownPlugin.configure({
options: { indentList: true },
});
export default function MarkdownDemo() {
const markdownEditor = usePlateEditor({
plugins: [markdownPlugin],
value: [{ children: [{ text: initialMarkdown }], type: 'p' }],
});
const [value, setValue] = useState<Value>([]);
const editor = usePlateEditor(
{
override: {
components: {
[BlockquotePlugin.key]: BlockquoteElement,
[BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),
[CodeBlockPlugin.key]: CodeBlockElement,
[CodeLinePlugin.key]: CodeLineElement,
[CodePlugin.key]: CodeLeaf,
[CodeSyntaxPlugin.key]: CodeSyntaxLeaf,
[HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }),
[HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }),
[HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }),
[HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }),
[HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }),
[HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }),
[HighlightPlugin.key]: HighlightLeaf,
[HorizontalRulePlugin.key]: HrElement,
[ImagePlugin.key]: ImageElement,
[ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),
[KbdPlugin.key]: KbdLeaf,
[LinkPlugin.key]: LinkElement,
[ParagraphPlugin.key]: ParagraphElement,
[StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),
[SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }),
[SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }),
[TableCellHeaderPlugin.key]: TableCellHeaderElement,
[TableCellPlugin.key]: TableCellElement,
[TablePlugin.key]: TableElement,
[TableRowPlugin.key]: TableRowElement,
[UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),
},
},
plugins: [
...basicNodesPlugins,
HorizontalRulePlugin,
linkPlugin,
tablePlugin,
...mediaPlugins,
InlineEquationPlugin,
HighlightPlugin,
KbdPlugin,
ImagePlugin,
...indentListPlugins,
autoformatPlugin,
markdownPlugin,
],
value: (editor) =>
deserializeMd(editor, initialMarkdown, {
processor(processor) {
return processor.use(remarkEmoji) as any;
},
}),
},
[]
);
useResetEditorOnChange({ editor, value: value }, [value]);
return (
<div className="grid grid-cols-2 overflow-y-auto">
<Plate
onValueChange={() => {
setValue(
editor.api.markdown.deserialize(
markdownEditor.api.markdown.serialize()
)
);
}}
editor={markdownEditor}
>
<EditorContainer>
<Editor variant="none" className="p-2 font-mono text-sm" />
</EditorContainer>
</Plate>
<Plate editor={editor}>
<EditorContainer className="bg-muted/50">
<Editor variant="none" className="p-2" />
</EditorContainer>
</Plate>
</div>
);
}
function useResetEditorOnChange(
{ editor, value }: { editor: PlateEditor; value: Value },
deps: any[]
) {
React.useEffect(() => {
if (value.length > 0) {
replaceNodeChildren(editor, {
at: [],
nodes: cloneDeep(value),
});
editor.history.undos = [];
editor.history.redos = [];
editor.operations = [];
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...deps]);
}
Slate 转 Markdown
当前支持的插件:段落、链接、列表、标题、斜体、粗体和代码。
const editor = createPlateEditor({
value,
plugins: [
// ...otherPlugins,
MarkdownPlugin,
],
});
const content = editor.api.markdown.serialize();
Plugins
MarkdownPlugin
Options
Collapse all
每个键是一个 Markdown 语法元素类型,值是一个转换函数。
每个键是一个 Markdown 语法文本类型,值是一个提供可选标记和转换函数的对象。
API
editor.api.markdown.deserialize
将 Markdown 字符串转换为 Slate 值。
Parameters
Collapse all
要反序列化的 Markdown 字符串。
Returns
Collapse all
一个 Slate 节点数组,表示反序列化的 Markdown 内容。
editor.api.markdown.serialize
将当前 Slate 值转换为 Markdown 字符串。
Parameters
Collapse all
Returns
Collapse all
一个表示序列化 Slate 内容的 Markdown 字符串。