快速开始

一个快速教程,帮助你快速上手 Plate。

创建项目

你可以选择以下模板之一来开始:

选项PlatePluginsAI后端
Notion-like template
Plate playground template
Plate minimal template

对于现有的 React 项目,请跳到下一步。

添加依赖

首先,安装核心依赖:

npm install @udecode/plate-common slate slate-dom slate-react slate-history

对于本指南中的示例,我们还将使用这些插件:

npm install @udecode/plate-basic-marks @udecode/plate-heading @udecode/plate-block-quote @udecode/cn
  • @udecode/plate-basic-marks provides bold, italic, underline, and code formatting.
  • @udecode/plate-heading adds h1-h6 support.
  • @udecode/plate-block-quote adds blockquote support.
  • @udecode/cn helps with component styling (optional).

基本编辑器

让我们从一个最小的编辑器设置开始。

import {
  usePlateEditor,
  Plate,
  PlateContent,
} from '@udecode/plate-common/react';
 
export default function BasicEditor() {
  const editor = usePlateEditor();
 
  return (
    <Plate editor={editor}>
      <PlateContent placeholder="Type..." />
    </Plate>
  );
}

Plate 管理编辑器状态,PlateContent 渲染编辑器内容。

Loading...
components/basic-editor-default-demo.tsx
'use client';

import React from 'react';

import {
  Plate,
  PlateContent,
  usePlateEditor,
} from '@udecode/plate-common/react';

export default function BasicEditorDefaultDemo() {
  const editor = usePlateEditor();

  return (
    <Plate editor={editor}>
      <PlateContent placeholder="Type..." />
    </Plate>
  );
}

样式

让我们给编辑器添加一些样式:EditorPlateContent 的样式版本。

Loading...
components/basic-editor-styling-demo.tsx
'use client';

import React from 'react';

import { Plate, usePlateEditor } from '@udecode/plate-common/react';

import { Editor, EditorContainer } from '@/components/plate-ui/editor';

export default function BasicEditorStylingDemo() {
  const editor = usePlateEditor();

  return (
    <Plate editor={editor}>
      <EditorContainer>
        <Editor placeholder="Type..." />
      </EditorContainer>
    </Plate>
  );
}

为了保持简单,我们将在以下代码片段中继续使用 PlateContent

初始化编辑器内容

让我们指定编辑器的初始内容:一个段落。

// ...
 
const value = [
  {
    type: 'p',
    children: [
      {
        text: 'This is editable plain text with react and history plugins, just like a <textarea>!',
      },
    ],
  },
];
 
export default function BasicEditor() {
  const editor = usePlateEditor({
    value,
  });
 
  return (
    <Plate editor={editor}>
      <PlateContent />
    </Plate>
  );
}
Loading...
components/basic-editor-value-demo.tsx
'use client';

import React from 'react';

import { Plate, usePlateEditor } from '@udecode/plate-common/react';

import { Editor, EditorContainer } from '@/components/plate-ui/editor';

const value = [
  {
    children: [
      {
        text: 'This is editable plain text with react and history plugins, just like a <textarea>!',
      },
    ],
    type: 'p',
  },
];

export default function BasicEditorValueDemo() {
  const editor = usePlateEditor({ value });

  return (
    <Plate editor={editor}>
      <EditorContainer>
        <Editor />
      </EditorContainer>
    </Plate>
  );
}

实现 Change Handler

在这一阶段,监控编辑器修改以适当存储值至关重要。onChange 属性将用于此目的。你还可以通过将值保存到本地存储或数据库并根据需要加载它来持久化编辑器状态。

// ...
 
export default function BasicEditor() {
  const localValue =
    typeof window !== 'undefined' && localStorage.getItem('editorContent');
 
  const editor = usePlateEditor({
    value: localValue ? JSON.parse(localValue) : value,
  });
 
  return (
    <Plate
      editor={editor}
      onChange={({ value }) => {
        // 为了性能,请对你的保存逻辑进行防抖处理
        localStorage.setItem('editorContent', JSON.stringify(value));
      }}
    >
      <PlateContent />
    </Plate>
  );
}
Loading...
components/basic-editor-handler-demo.tsx
'use client';

import React, { useState } from 'react';

import type { Value } from '@udecode/plate-common';

import { Plate, usePlateEditor } from '@udecode/plate-common/react';

import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from '@/components/ui/accordion';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';

const value = [
  {
    children: [
      {
        text: 'This is editable plain text with react and history plugins, just like a textarea!',
      },
    ],
    type: 'p',
  },
];

export default function BasicEditorHandlerDemo() {
  const [debugValue, setDebugValue] = useState<Value>(value);

  const localValue =
    typeof window !== 'undefined' && localStorage.getItem('editorContent');

  const editor = usePlateEditor({
    value: localValue ? JSON.parse(localValue) : value,
  });

  return (
    <Plate
      onChange={({ value }) => {
        localStorage.setItem('editorContent', JSON.stringify(value));
        setDebugValue(value);
      }}
      editor={editor}
    >
      <EditorContainer>
        <Editor />
      </EditorContainer>

      <Accordion type="single" collapsible>
        <AccordionItem value="manual-installation">
          <AccordionTrigger>Debug Value</AccordionTrigger>
          <AccordionContent>{JSON.stringify(debugValue)}</AccordionContent>
        </AccordionItem>
      </Accordion>
    </Plate>
  );
}

Plugins

让我们使用一些基本的插件。

// ...
 
import {
  BoldPlugin,
  ItalicPlugin,
  UnderlinePlugin,
} from '@udecode/plate-basic-marks/react';
import { HeadingPlugin } from '@udecode/plate-heading/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
 
const value = [
  // ...
];
 
export default function BasicEditor() {
  const editor = usePlateEditor({
    plugins: [
      HeadingPlugin,
      BlockquotePlugin,
      BoldPlugin,
      ItalicPlugin,
      UnderlinePlugin,
    ],
    value,
  });
 
  return (
    <Plate editor={editor}>
      <PlateContent />
    </Plate>
  );
}
Loading...
components/basic-plugins-default-demo.tsx
'use client';

import React, { useState } from 'react';

import type { Value } from '@udecode/plate-common';

import {
  BoldPlugin,
  CodePlugin,
  ItalicPlugin,
  UnderlinePlugin,
} from '@udecode/plate-basic-marks/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
import { Plate, usePlateEditor } from '@udecode/plate-common/react';
import { HeadingPlugin } from '@udecode/plate-heading/react';

import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from '@/components/ui/accordion';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';

import { basicEditorValue } from './basic-plugins-components-demo';

export default function BasicPluginsDefaultDemo() {
  const [debugValue, setDebugValue] = useState<Value>(basicEditorValue);
  const editor = usePlateEditor({
    plugins: [
      BlockquotePlugin,
      HeadingPlugin,
      BoldPlugin,
      ItalicPlugin,
      UnderlinePlugin,
      CodePlugin,
    ],
    value: basicEditorValue,
  });

  return (
    <Plate
      onChange={({ value }) => {
        setDebugValue(value);
        // save newValue...
      }}
      editor={editor}
    >
      <EditorContainer>
        <Editor />
      </EditorContainer>

      <Accordion type="single" collapsible>
        <AccordionItem value="manual-installation">
          <AccordionTrigger>Debug Value</AccordionTrigger>
          <AccordionContent>{JSON.stringify(debugValue)}</AccordionContent>
        </AccordionItem>
      </Accordion>
    </Plate>
  );
}

插件功能正常。然而,由于我们没有为渲染指定任何自定义组件,编辑器使用默认(未样式化)组件。具体来说,默认元素组件是一个 div,默认叶组件是一个 span

Components

为了在一个地方插件所有组件,使用 override.components 选项在 usePlateEditor 中。我们将使用 withProps 助手传递额外的 Tailwind CSS 类。

// ...
 
import { withProps } from '@udecode/cn';
import {
  Plate,
  PlateElement,
  PlateLeaf,
  usePlateEditor,
} from '@udecode/plate-common/react';
 
export default function BasicEditor() {
  const editor = usePlateEditor({
    plugins: [
      HeadingPlugin,
      BlockquotePlugin,
      BoldPlugin,
      ItalicPlugin,
      UnderlinePlugin,
    ],
    override: {
      components: {
        blockquote: withProps(PlateElement, {
          as: 'blockquote',
          className: 'mb-4 border-l-4 border-[#d0d7de] pl-4 text-[#636c76]',
        }),
        bold: withProps(PlateLeaf, { as: 'strong' }),
        h1: withProps(PlateElement, {
          as: 'h1',
          className:
            'mb-4 mt-6 text-3xl font-semibold tracking-tight lg:text-4xl',
        }),
        h2: withProps(PlateElement, {
          as: 'h2',
          className: 'mb-4 mt-6 text-2xl font-semibold tracking-tight',
        }),
        h3: withProps(PlateElement, {
          as: 'h3',
          className: 'mb-4 mt-6 text-xl font-semibold tracking-tight',
        }),
        italic: withProps(PlateLeaf, { as: 'em' }),
        p: withProps(PlateElement, {
          as: 'p',
          className: 'mb-4',
        }),
        underline: withProps(PlateLeaf, { as: 'u' }),
      },
    },
  });
 
  return (
    <Plate editor={editor}>
      <PlateContent />
    </Plate>
  );
}
Loading...
components/basic-plugins-components-demo.tsx
'use client';

import { withProps } from '@udecode/cn';
import {
  BoldPlugin,
  CodePlugin,
  ItalicPlugin,
  UnderlinePlugin,
} from '@udecode/plate-basic-marks/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
import {
  Plate,
  PlateElement,
  PlateLeaf,
  usePlateEditor,
} from '@udecode/plate-common/react';
import { HeadingPlugin } from '@udecode/plate-heading/react';

import { Editor, EditorContainer } from '@/components/plate-ui/editor';

export default function BasicPluginsComponentsDemo() {
  const editor = usePlateEditor({
    override: {
      components: {
        blockquote: withProps(PlateElement, {
          as: 'blockquote',
          className: 'mb-4 border-l-4 border-[#d0d7de] pl-4 text-[#636c76]',
        }),
        bold: withProps(PlateLeaf, { as: 'strong' }),
        h1: withProps(PlateElement, {
          as: 'h1',
          className:
            'mb-4 mt-6 text-3xl font-semibold tracking-tight lg:text-4xl',
        }),
        h2: withProps(PlateElement, {
          as: 'h2',
          className: 'mb-4 mt-6 text-2xl font-semibold tracking-tight',
        }),
        h3: withProps(PlateElement, {
          as: 'h3',
          className: 'mb-4 mt-6 text-xl font-semibold tracking-tight',
        }),
        italic: withProps(PlateLeaf, { as: 'em' }),
        p: withProps(PlateElement, {
          as: 'p',
          className: 'mb-4',
        }),
        underline: withProps(PlateLeaf, { as: 'u' }),
      },
    },
    plugins: [
      BlockquotePlugin,
      HeadingPlugin,
      BoldPlugin,
      ItalicPlugin,
      UnderlinePlugin,
      CodePlugin,
    ],
    value: basicEditorValue,
  });

  return (
    <Plate editor={editor}>
      <EditorContainer>
        <Editor placeholder="Type..." autoFocus={false} spellCheck={false} />
      </EditorContainer>
    </Plate>
  );
}

export const basicEditorValue = [
  {
    id: '1',
    children: [
      {
        text: '🌳 Blocks',
      },
    ],
    type: 'h1',
  },
  {
    id: '2',
    children: [
      {
        text: 'Easily create headings of various levels, from H1 to H6, to structure your content and make it more organized.',
      },
    ],
    type: 'p',
  },
  {
    id: '3',
    children: [
      {
        text: 'Create blockquotes to emphasize important information or highlight quotes from external sources.',
      },
    ],
    type: 'blockquote',
  },
  {
    id: '1',
    children: [
      {
        text: '🌱 Marks',
      },
    ],
    type: 'h1',
  },
  {
    id: '2',
    children: [
      {
        text: 'Add style and emphasis to your text using the mark plugins, which offers a variety of formatting options.',
      },
    ],
    type: 'p',
  },
  {
    id: '3',
    children: [
      {
        text: 'Make text ',
      },
      {
        bold: true,
        text: 'bold',
      },
      {
        text: ', ',
      },
      {
        italic: true,
        text: 'italic',
      },
      {
        text: ', ',
      },
      {
        text: 'underlined',
        underline: true,
      },
      {
        text: ', or apply a ',
      },
      {
        bold: true,
        italic: true,
        text: 'combination',
        underline: true,
      },
      {
        text: ' of these styles for a visually striking effect.',
      },
    ],
    type: 'p',
  },
];

使用 HTML 字符串初始化编辑器内容

你还可以使用 HTML 字符串和相应的插件指定编辑器的初始内容。

// ...
 
const htmlValue = '<p>This is <b>bold</b> and <i>italic</i> text!</p>';
 
export default function BasicEditor() {
  const editor = usePlateEditor({
    // ...
    value: htmlValue,
  });
 
  return (
    <Plate editor={editor}>
      <PlateContent />
    </Plate>
  );
}

就这样!

你现在可以玩转 Playground 并开始构建你自己的编辑器。