Docs
Getting Started

Getting Started

A quick tutorial to get you up and running with Plate.

Create project

You can choose one of the following templates to get started:

OptionNextJSPlatePluginsAI & Backend
Notion clone template
Plate playground template
Plate minimal template
NextJS template

For an existing project, jump to the next step.

Add dependencies

Install the core and the plugins you need. You need at least:

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

Alternatively you can install @udecode/plate that contains all the packages excluding the ones with heavy dependencies (e.g. @udecode/plate-dnd).

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

Basic Editor

Let's start with a minimal editor setup.

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 manages the editor state and PlateContent renders the editor content.



Styling

Let's give our editor some styles: Editor is a styled version of PlateContent.



Initializing Editor's Value

Let's specify the initial content of the editor: a single paragraph.

// ...
 
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>
  );
}
This is editable plain text with react and history plugins, just like a <textarea>!

Implementing Change Handler

At this stage, it's crucial to monitor editor modifications in order to store the values appropriately. The onChange prop will serve this purpose. You can also persist the editor's state by saving the value to local storage or a database and loading it back when needed.

// ...
 
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>
  );
}
This is editable plain text with react and history plugins, just like a textarea!

Plugins

Let's use the basic plugins for a rich-text editor.

// ...
 
import {
  BoldPlugin,
  ItalicPlugin,
  UnderlinePlugin,
  CodePlugin,
} from '@udecode/plate-basic-marks/react';
import { HeadingPlugin } from '@udecode/plate-heading/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
import { CodeBlockPlugin } from '@udecode/plate-code-block/react';
 
const value = [
  // ...
];
 
export default function BasicEditor() {
  const editor = usePlateEditor({
    value,
    plugins: [
      HeadingPlugin,
      BlockquotePlugin,
      CodeBlockPlugin,
      BoldPlugin,
      ItalicPlugin,
      UnderlinePlugin,
      CodePlugin,
    ],
  });
 
  return (
    <Plate editor={editor}>
      <PlateContent />
    </Plate>
  );
}
🌳 Blocks
Easily create headings of various levels, from H1 to H6, to structure your content and make it more organized.
Create blockquotes to emphasize important information or highlight quotes from external sources.
// Use code blocks to showcase code snippets
function greet() {
console.info('Hello World!');
}
🌱 Marks
Add style and emphasis to your text using the mark plugins, which offers a variety of formatting options.
Make text bold, italic, underlined, or apply a combination of these styles for a visually striking effect.
Add strikethrough to indicate deleted or outdated content.
Write code snippets with inline code formatting for easy readability.
Press ⌘+B to apply bold mark or ⌘+I for italic mark.

The plugins are functioning correctly. However, since we haven't specified any custom components for rendering, the editor is using the default (unstyled) components. Specifically, the default element component is a div, and the default leaf component is a span.

Components

To plug-in all the components in one place, we can use the override.components option in usePlateEditor:

// ...
 
// Import Prism for code highlighting
import Prism from 'prismjs';
 
// This is a local file, you will need to create this file in your project
import { createPlateUI } from '@/lib/create-plate-ui';
 
export default function BasicEditor() {
  const editor = usePlateEditor({
    plugins: [
      HeadingPlugin,
      BlockquotePlugin,
      CodeBlockPlugin.configure({ options: { prism: Prism } }),
      BoldPlugin,
      ItalicPlugin,
      UnderlinePlugin,
      CodePlugin,
    ],
    override: {
      components: createPlateUI(),
    },
  });
 
  return (
    <Plate editor={editor}>
      <PlateContent />
    </Plate>
  );
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { withProps } from '@udecode/cn';
import { AIPlugin } from '@udecode/plate-ai/react';
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 { CommentsPlugin } from '@udecode/plate-comments/react';
import { ParagraphPlugin } from '@udecode/plate-common/react';
import {
  type NodeComponent,
  PlateElement,
  PlateLeaf,
} from '@udecode/plate-common/react';
import { DatePlugin } from '@udecode/plate-date/react';
import { EmojiInputPlugin } from '@udecode/plate-emoji/react';
import { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react';
import { FindReplacePlugin } from '@udecode/plate-find-replace';
import { HEADING_KEYS } from '@udecode/plate-heading';
import { TocPlugin } from '@udecode/plate-heading/react';
import { HighlightPlugin } from '@udecode/plate-highlight/react';
import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';
import { KbdPlugin } from '@udecode/plate-kbd/react';
import { ColumnItemPlugin, ColumnPlugin } from '@udecode/plate-layout/react';
import { LinkPlugin } from '@udecode/plate-link/react';
import {
  BulletedListPlugin,
  ListItemPlugin,
  NumberedListPlugin,
  TodoListPlugin,
} from '@udecode/plate-list/react';
import { ImagePlugin, MediaEmbedPlugin } from '@udecode/plate-media/react';
import {
  MentionInputPlugin,
  MentionPlugin,
} from '@udecode/plate-mention/react';
import { SlashInputPlugin } from '@udecode/plate-slash-command/react';
import {
  TableCellHeaderPlugin,
  TableCellPlugin,
  TablePlugin,
  TableRowPlugin,
} from '@udecode/plate-table/react';
import { TogglePlugin } from '@udecode/plate-toggle/react';
 
import { AILeaf } from '@/registry/default/plate-ui/ai-leaf';
import { BlockquoteElement } from '@/registry/default/plate-ui/blockquote-element';
import { CodeBlockElement } from '@/registry/default/plate-ui/code-block-element';
import { CodeLeaf } from '@/registry/default/plate-ui/code-leaf';
import { CodeLineElement } from '@/registry/default/plate-ui/code-line-element';
import { CodeSyntaxLeaf } from '@/registry/default/plate-ui/code-syntax-leaf';
import { ColumnElement } from '@/registry/default/plate-ui/column-element';
import { ColumnGroupElement } from '@/registry/default/plate-ui/column-group-element';
import { CommentLeaf } from '@/registry/default/plate-ui/comment-leaf';
import { DateElement } from '@/registry/default/plate-ui/date-element';
import { EmojiInputElement } from '@/registry/default/plate-ui/emoji-input-element';
import { ExcalidrawElement } from '@/registry/default/plate-ui/excalidraw-element';
import { HeadingElement } from '@/registry/default/plate-ui/heading-element';
import { HighlightLeaf } from '@/registry/default/plate-ui/highlight-leaf';
import { HrElement } from '@/registry/default/plate-ui/hr-element';
import { ImageElement } from '@/registry/default/plate-ui/image-element';
import { KbdLeaf } from '@/registry/default/plate-ui/kbd-leaf';
import { LinkElement } from '@/registry/default/plate-ui/link-element';
import { ListElement } from '@/registry/default/plate-ui/list-element';
import { MediaEmbedElement } from '@/registry/default/plate-ui/media-embed-element';
import { MentionElement } from '@/registry/default/plate-ui/mention-element';
import { MentionInputElement } from '@/registry/default/plate-ui/mention-input-element';
import { ParagraphElement } from '@/registry/default/plate-ui/paragraph-element';
import { withPlaceholders } from '@/registry/default/plate-ui/placeholder';
import { SearchHighlightLeaf } from '@/registry/default/plate-ui/search-highlight-leaf';
import { SlashInputElement } from '@/registry/default/plate-ui/slash-input-element';
import {
  TableCellElement,
  TableCellHeaderElement,
} from '@/registry/default/plate-ui/table-cell-element';
import { TableElement } from '@/registry/default/plate-ui/table-element';
import { TableRowElement } from '@/registry/default/plate-ui/table-row-element';
import { TocElement } from '@/registry/default/plate-ui/toc-element';
import { TodoListElement } from '@/registry/default/plate-ui/todo-list-element';
import { ToggleElement } from '@/registry/default/plate-ui/toggle-element';
import { withDraggables } from '@/registry/default/plate-ui/with-draggables';
 
export const createPlateUI = ({
  draggable,
  placeholder,
}: { draggable?: boolean; placeholder?: boolean } = {}) => {
  let components: Record<string, NodeComponent> = {
    [AIPlugin.key]: AILeaf,
    [BlockquotePlugin.key]: BlockquoteElement,
    [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),
    [BulletedListPlugin.key]: withProps(ListElement, { variant: 'ul' }),
    [CodeBlockPlugin.key]: CodeBlockElement,
    [CodeLinePlugin.key]: CodeLineElement,
    [CodePlugin.key]: CodeLeaf,
    [CodeSyntaxPlugin.key]: CodeSyntaxLeaf,
    [ColumnItemPlugin.key]: ColumnElement,
    [ColumnPlugin.key]: ColumnGroupElement,
    [CommentsPlugin.key]: CommentLeaf,
    [DatePlugin.key]: DateElement,
    [EmojiInputPlugin.key]: EmojiInputElement,
    [ExcalidrawPlugin.key]: ExcalidrawElement,
    [FindReplacePlugin.key]: SearchHighlightLeaf,
    [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,
    [ListItemPlugin.key]: withProps(PlateElement, { as: 'li' }),
    [MediaEmbedPlugin.key]: MediaEmbedElement,
    [MentionInputPlugin.key]: MentionInputElement,
    [MentionPlugin.key]: MentionElement,
    [NumberedListPlugin.key]: withProps(ListElement, { variant: 'ol' }),
    [ParagraphPlugin.key]: ParagraphElement,
    [SlashInputPlugin.key]: SlashInputElement,
    [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,
    [TocPlugin.key]: TocElement,
    [TodoListPlugin.key]: TodoListElement,
    [TogglePlugin.key]: ToggleElement,
    [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),
  };
 
  if (placeholder) {
    components = withPlaceholders(components);
  }
  if (draggable) {
    components = withDraggables(components);
  }
 
  return components;
};

🌳 Blocks

Easily create headings of various levels, from H1 to H6, to structure your content and make it more organized.
Create blockquotes to emphasize important information or highlight quotes from external sources.
// Use code blocks to showcase code snippets
function greet() {
console.info('Hello World!');
}

🌱 Marks

Add style and emphasis to your text using the mark plugins, which offers a variety of formatting options.
Make text bold, italic, underlined, or apply a combination of these styles for a visually striking effect.
Add strikethrough to indicate deleted or outdated content.
Write code snippets with inline code formatting for easy readability.
Press ⌘+B to apply bold mark or ⌘+I for italic mark.

Initializing Editor's Value with HTML String

You can also specify the initial content of the editor using an HTML string and the corresponding plugins.

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

That's it!

You can now play around with the Playground and start building your own editor.