Components
Floating Toolbar buttons
Floating Toolbar buttons
Display toolbar buttons that dynamically adjust their position while scrolling.
Installation
npx @udecode/plate-ui@latest add floating-toolbar-buttons
Examples
🌳 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 snippetsfunction 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.'use client';
import React, { useRef } from 'react';
import { cn, withProps } from '@udecode/cn';
import { AlignPlugin } from '@udecode/plate-alignment';
import { AutoformatPlugin } from '@udecode/plate-autoformat/react';
import {
BoldPlugin,
CodePlugin,
ItalicPlugin,
StrikethroughPlugin,
SubscriptPlugin,
SuperscriptPlugin,
UnderlinePlugin,
} from '@udecode/plate-basic-marks/react';
import { BlockquotePlugin } from '@udecode/plate-block-quote/react';
import { ExitBreakPlugin, SoftBreakPlugin } from '@udecode/plate-break/react';
import { CaptionPlugin } from '@udecode/plate-caption/react';
import {
isCodeBlockEmpty,
isSelectionAtCodeBlockStart,
unwrapCodeBlock,
} from '@udecode/plate-code-block';
import {
CodeBlockPlugin,
CodeLinePlugin,
CodeSyntaxPlugin,
} from '@udecode/plate-code-block/react';
import { CommentsPlugin } from '@udecode/plate-comments/react';
import {
isBlockAboveEmpty,
isSelectionAtBlockStart,
someNode,
} from '@udecode/plate-common';
import {
createPlateEditor,
ParagraphPlugin,
Plate,
PlateLeaf,
} from '@udecode/plate-common/react';
import { DndPlugin } from '@udecode/plate-dnd';
import { DocxPlugin } from '@udecode/plate-docx';
import { EmojiPlugin } from '@udecode/plate-emoji/react';
import { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react';
import {
FontBackgroundColorPlugin,
FontColorPlugin,
FontSizePlugin,
} from '@udecode/plate-font';
import { HEADING_KEYS, HEADING_LEVELS } from '@udecode/plate-heading';
import { HeadingPlugin } from '@udecode/plate-heading/react';
import { HighlightPlugin } from '@udecode/plate-highlight/react';
import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react';
import { IndentListPlugin } from '@udecode/plate-indent-list/react';
import { IndentPlugin } from '@udecode/plate-indent/react';
import { JuicePlugin } from '@udecode/plate-juice';
import { KbdPlugin } from '@udecode/plate-kbd/react';
import { LineHeightPlugin } from '@udecode/plate-line-height';
import { LinkPlugin } from '@udecode/plate-link/react';
import { TodoListPlugin } from '@udecode/plate-list/react';
import { MarkdownPlugin } from '@udecode/plate-markdown';
import { ImagePlugin, MediaEmbedPlugin } from '@udecode/plate-media/react';
import {
MentionInputPlugin,
MentionPlugin,
} from '@udecode/plate-mention/react';
import { NodeIdPlugin } from '@udecode/plate-node-id';
import { ResetNodePlugin } from '@udecode/plate-reset-node/react';
import { SelectOnBackspacePlugin } from '@udecode/plate-select';
import { BlockSelectionPlugin } from '@udecode/plate-selection/react';
import { TabbablePlugin } from '@udecode/plate-tabbable/react';
import {
TableCellHeaderPlugin,
TableCellPlugin,
TablePlugin,
TableRowPlugin,
} from '@udecode/plate-table/react';
import { TrailingBlockPlugin } from '@udecode/plate-trailing-block';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { autoformatRules } from '@/lib/plate/autoformat-rules';
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 { CommentLeaf } from '@/components/plate-ui/comment-leaf';
import { CommentsPopover } from '@/components/plate-ui/comments-popover';
import {
CursorOverlay,
DragOverCursorPlugin,
} from '@/components/plate-ui/cursor-overlay';
import { Editor } from '@/components/plate-ui/editor';
import { ExcalidrawElement } from '@/components/plate-ui/excalidraw-element';
import { FixedToolbar } from '@/components/plate-ui/fixed-toolbar';
import { FixedToolbarButtons } from '@/components/plate-ui/fixed-toolbar-buttons';
import { FloatingToolbar } from '@/components/plate-ui/floating-toolbar';
import { FloatingToolbarButtons } from '@/components/plate-ui/floating-toolbar-buttons';
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 { LinkFloatingToolbar } from '@/components/plate-ui/link-floating-toolbar';
import { MediaEmbedElement } from '@/components/plate-ui/media-embed-element';
import { MentionElement } from '@/components/plate-ui/mention-element';
import { MentionInputElement } from '@/components/plate-ui/mention-input-element';
import { ParagraphElement } from '@/components/plate-ui/paragraph-element';
import { withPlaceholders } from '@/components/plate-ui/placeholder';
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';
import { TodoListElement } from '@/components/plate-ui/todo-list-element';
import { withDraggables } from '@/components/plate-ui/with-draggables';
export default function PlateEditor() {
const containerRef = useRef(null);
const editor = useMyEditor();
return (
<DndProvider backend={HTML5Backend}>
<Plate editor={editor}>
<div
ref={containerRef}
className={cn(
'relative',
// Block selection
'[&_.slate-start-area-left]:!w-[64px] [&_.slate-start-area-right]:!w-[64px] [&_.slate-start-area-top]:!h-4'
)}
>
<FixedToolbar>
<FixedToolbarButtons />
</FixedToolbar>
<Editor
className="px-[96px] py-16"
autoFocus
focusRing={false}
variant="ghost"
size="md"
/>
<FloatingToolbar>
<FloatingToolbarButtons />
</FloatingToolbar>
<CommentsPopover />
<CursorOverlay containerRef={containerRef} />
</div>
</Plate>
</DndProvider>
);
}
export const useMyEditor = () => {
const editor = createPlateEditor({
plugins: [
// Nodes
HeadingPlugin,
BlockquotePlugin,
CodeBlockPlugin,
CodeLinePlugin,
CodeSyntaxPlugin,
HorizontalRulePlugin,
LinkPlugin.configure({
render: { afterEditable: () => <LinkFloatingToolbar /> },
}),
ImagePlugin,
MediaEmbedPlugin,
CaptionPlugin.configure({
options: { plugins: [ImagePlugin, MediaEmbedPlugin] },
}),
MentionPlugin,
MentionInputPlugin,
TablePlugin,
TableRowPlugin,
TableCellPlugin,
TableCellHeaderPlugin,
TodoListPlugin,
ExcalidrawPlugin,
// Marks
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
StrikethroughPlugin,
CodePlugin,
SubscriptPlugin,
SuperscriptPlugin,
FontColorPlugin,
FontBackgroundColorPlugin,
FontSizePlugin,
HighlightPlugin,
KbdPlugin,
// Block Style
AlignPlugin.configure({
inject: {
targetPlugins: [ParagraphPlugin.key, ...HEADING_LEVELS],
},
}),
IndentPlugin.configure({
inject: {
targetPlugins: [
ParagraphPlugin.key,
BlockquotePlugin.key,
CodeBlockPlugin.key,
...HEADING_LEVELS,
],
},
}),
IndentListPlugin.configure({
inject: {
targetPlugins: [
ParagraphPlugin.key,
BlockquotePlugin.key,
CodeBlockPlugin.key,
...HEADING_LEVELS,
],
},
}),
LineHeightPlugin.configure({
inject: {
nodeProps: {
defaultNodeValue: 1.5,
validNodeValues: [1, 1.2, 1.5, 2, 3],
},
targetPlugins: [ParagraphPlugin.key, ...HEADING_LEVELS],
},
}),
// Functionality
AutoformatPlugin.configure({
options: {
rules: autoformatRules,
enableUndoOnDelete: true,
},
}),
BlockSelectionPlugin,
DndPlugin.configure({
options: { enableScroller: true },
}),
EmojiPlugin,
ExitBreakPlugin.configure({
options: {
rules: [
{
hotkey: 'mod+enter',
},
{
hotkey: 'mod+shift+enter',
before: true,
},
{
hotkey: 'enter',
query: {
start: true,
end: true,
allow: HEADING_LEVELS,
},
relative: true,
level: 1,
},
],
},
}),
NodeIdPlugin,
ResetNodePlugin.configure({
options: {
rules: [
{
types: [BlockquotePlugin.key, TodoListPlugin.key],
defaultType: ParagraphPlugin.key,
hotkey: 'Enter',
predicate: isBlockAboveEmpty,
},
{
types: [BlockquotePlugin.key, TodoListPlugin.key],
defaultType: ParagraphPlugin.key,
hotkey: 'Backspace',
predicate: isSelectionAtBlockStart,
},
{
types: [CodeBlockPlugin.key],
defaultType: ParagraphPlugin.key,
onReset: unwrapCodeBlock,
hotkey: 'Enter',
predicate: isCodeBlockEmpty,
},
{
types: [CodeBlockPlugin.key],
defaultType: ParagraphPlugin.key,
onReset: unwrapCodeBlock,
hotkey: 'Backspace',
predicate: isSelectionAtCodeBlockStart,
},
],
},
}),
SelectOnBackspacePlugin.configure({
options: {
query: {
allow: [ImagePlugin.key, HorizontalRulePlugin.key],
},
},
}),
SoftBreakPlugin.configure({
options: {
rules: [
{ hotkey: 'shift+enter' },
{
hotkey: 'enter',
query: {
allow: [
CodeBlockPlugin.key,
BlockquotePlugin.key,
TableCellPlugin.key,
TableCellHeaderPlugin.key,
],
},
},
],
},
}),
TabbablePlugin.configure(({ editor }) => ({
options: {
query: () => {
if (isSelectionAtBlockStart(editor)) return false;
return !someNode(editor, {
match: (n) => {
return !!(
n.type &&
([
TablePlugin.key,
TodoListPlugin.key,
CodeBlockPlugin.key,
].includes(n.type as string) ||
n.listStyleType)
);
},
});
},
},
})),
TrailingBlockPlugin.configure({
options: { type: ParagraphPlugin.key },
}),
DragOverCursorPlugin,
// Collaboration
CommentsPlugin.configure({
options: {
users: {
1: {
id: '1',
name: 'zbeyens',
avatarUrl:
'https://avatars.githubusercontent.com/u/19695832?s=96&v=4',
},
},
myUserId: '1',
},
}),
// Deserialization
DocxPlugin,
MarkdownPlugin,
JuicePlugin,
],
override: {
components: withDraggables(
withPlaceholders({
[BlockquotePlugin.key]: BlockquoteElement,
[CodeBlockPlugin.key]: CodeBlockElement,
[CodeLinePlugin.key]: CodeLineElement,
[CodeSyntaxPlugin.key]: CodeSyntaxLeaf,
[HorizontalRulePlugin.key]: HrElement,
[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' }),
[ImagePlugin.key]: ImageElement,
[LinkPlugin.key]: LinkElement,
[MediaEmbedPlugin.key]: MediaEmbedElement,
[MentionPlugin.key]: MentionElement,
[MentionInputPlugin.key]: MentionInputElement,
[ParagraphPlugin.key]: ParagraphElement,
[TablePlugin.key]: TableElement,
[TableRowPlugin.key]: TableRowElement,
[TableCellPlugin.key]: TableCellElement,
[TableCellHeaderPlugin.key]: TableCellHeaderElement,
[TodoListPlugin.key]: TodoListElement,
[ExcalidrawPlugin.key]: ExcalidrawElement,
[BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }),
[CodePlugin.key]: CodeLeaf,
[HighlightPlugin.key]: HighlightLeaf,
[ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }),
[KbdPlugin.key]: KbdLeaf,
[StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }),
[SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }),
[SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }),
[UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }),
[CommentsPlugin.key]: CommentLeaf,
})
),
},
value: [
{
id: '1',
type: ParagraphPlugin.key,
children: [{ text: 'Hello, World!' }],
},
],
});
return editor;
};