Next.js
在 Next.js 中安装和配置 Plate UI
本指南将引导您在 Next.js 应用程序中逐步构建 Plate 编辑器。
创建您的第一个编辑器
首先将核心 Editor 组件添加到您的项目中:
pnpm dlx shadcn@latest add @plate/editor
接下来,创建一个基础编辑器页面。此示例在 EditorContainer 中设置了一个简单的编辑器。
app/editor/page.tsx
'use client';
import { Plate, usePlateEditor } from 'platejs/react';
import { Editor, EditorContainer } from '@/components/ui/editor';
export default function MyEditorPage() {
const editor = usePlateEditor(); // 初始化编辑器实例
return (
<Plate editor={editor}> {/* 提供编辑器上下文 */}
<EditorContainer> {/* 设置编辑器区域样式 */}
<Editor placeholder="在这里输入精彩内容..." />
</EditorContainer>
</Plate>
);
}usePlateEditor 创建一个记忆化的编辑器实例,确保在重新渲染时保持稳定。如需非记忆化版本,请使用 createPlateEditor。
Loading...
Files
components/installation-next-01-editor-demo.tsx
'use client';
import { Plate, usePlateEditor } from 'platejs/react';
import { Editor, EditorContainer } from '@/components/ui/editor';
export default function MyEditorPage() {
const editor = usePlateEditor();
return (
<Plate editor={editor}>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}
添加基础标记
通过文本格式化增强您的编辑器。添加 Basic Nodes Kit、FixedToolbar 和 MarkToolbarButton 组件:
pnpm dlx shadcn@latest add @plate/basic-nodes-kit @plate/fixed-toolbar @plate/mark-toolbar-button
basic-nodes-kit 包含所有基础插件(粗体、斜体、下划线、标题、引用等)及其组件,我们将在后续步骤中使用。
更新您的编辑器页面以包含这些组件和基础标记插件。 此示例添加了粗体、斜体和下划线功能。
app/editor/page.tsx
'use client';
import * as React from 'react';
import type { Value } from 'platejs';
import {
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
} from '@platejs/basic-nodes/react';
import {
Plate,
usePlateEditor,
} from 'platejs/react';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
const initialValue: Value = [
{
type: 'p',
children: [
{ text: 'Hello! Try out the ' },
{ text: 'bold', bold: true },
{ text: ', ' },
{ text: 'italic', italic: true },
{ text: ', and ' },
{ text: 'underline', underline: true },
{ text: ' formatting.' },
],
},
];
export default function MyEditorPage() {
const editor = usePlateEditor({
plugins: [BoldPlugin, ItalicPlugin, UnderlinePlugin], // Add the mark plugins
value: initialValue, // Set initial content
});
return (
<Plate editor={editor}>
<FixedToolbar className="justify-start rounded-t-lg">
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">B</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">I</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">U</MarkToolbarButton>
</FixedToolbar>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}Loading...
Files
components/installation-next-02-marks-demo.tsx
'use client';
import * as React from 'react';
import type { Value } from 'platejs';
import {
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
} from '@platejs/basic-nodes/react';
import { Plate, usePlateEditor } from 'platejs/react';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
// import { Bold, Italic, Underline } from 'lucide-react'; // Example icons
const initialValue: Value = [
{
children: [
{ text: 'Hello! Try out the ' },
{ bold: true, text: 'bold' },
{ text: ', ' },
{ italic: true, text: 'italic' },
{ text: ', and ' },
{ text: 'underline', underline: true },
{ text: ' formatting.' },
],
type: 'p',
},
];
export default function MyEditorPage() {
const editor = usePlateEditor({
plugins: [BoldPlugin, ItalicPlugin, UnderlinePlugin],
value: initialValue,
});
return (
<Plate editor={editor}>
<FixedToolbar className="justify-start rounded-t-lg">
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">
B
</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">
I
</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">
U
</MarkToolbarButton>
</FixedToolbar>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}
添加基础元素
引入块级元素,如标题和引用块,并使用自定义组件。
app/editor/page.tsx
'use client';
import * as React from 'react';
import type { Value } from 'platejs';
import {
BlockquotePlugin,
BoldPlugin,
H1Plugin,
H2Plugin,
H3Plugin,
ItalicPlugin,
UnderlinePlugin,
} from '@platejs/basic-nodes/react';
import {
Plate,
usePlateEditor,
} from 'platejs/react';
import { BlockquoteElement } from '@/components/ui/blockquote-node';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
import { H1Element, H2Element, H3Element } from '@/components/ui/heading-node';
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
import { ToolbarButton } from '@/components/ui/toolbar'; // Generic toolbar button
const initialValue: Value = [
{
children: [{ text: 'Title' }],
type: 'h3',
},
{
children: [{ text: 'This is a quote.' }],
type: 'blockquote',
},
{
children: [
{ text: 'With some ' },
{ bold: true, text: 'bold' },
{ text: ' text for emphasis!' },
],
type: 'p',
},
];
export default function MyEditorPage() {
const editor = usePlateEditor({
plugins: [
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
H1Plugin.withComponent(H1Element),
H2Plugin.withComponent(H2Element),
H3Plugin.withComponent(H3Element),
BlockquotePlugin.withComponent(BlockquoteElement),
],
value: initialValue,
});
return (
<Plate editor={editor}>
<FixedToolbar className="flex justify-start gap-1 rounded-t-lg">
{/* Element Toolbar Buttons */}
<ToolbarButton onClick={() => editor.tf.h1.toggle()}>H1</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h2.toggle()}>H2</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h3.toggle()}>H3</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.blockquote.toggle()}>Quote</ToolbarButton>
{/* Mark Toolbar Buttons */}
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">B</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">I</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">U</MarkToolbarButton>
</FixedToolbar>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}Loading...
Files
components/installation-next-03-elements-demo.tsx
'use client';
import * as React from 'react';
import type { Value } from 'platejs';
import {
BlockquotePlugin,
BoldPlugin,
H1Plugin,
H2Plugin,
H3Plugin,
ItalicPlugin,
UnderlinePlugin,
} from '@platejs/basic-nodes/react';
import { Plate, usePlateEditor } from 'platejs/react';
import { BlockquoteElement } from '@/components/ui/blockquote-node';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
import { H1Element, H2Element, H3Element } from '@/components/ui/heading-node';
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
import { ToolbarButton } from '@/components/ui/toolbar';
const initialValue: Value = [
{
children: [{ text: 'Title' }],
type: 'h3',
},
{
children: [{ text: 'This is a quote.' }],
type: 'blockquote',
},
{
children: [
{ text: 'With some ' },
{ bold: true, text: 'bold' },
{ text: ' text for emphasis!' },
],
type: 'p',
},
];
export default function MyEditorPage() {
const editor = usePlateEditor({
plugins: [
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
H1Plugin.withComponent(H1Element),
H2Plugin.withComponent(H2Element),
H3Plugin.withComponent(H3Element),
BlockquotePlugin.withComponent(BlockquoteElement),
],
value: initialValue,
});
return (
<Plate editor={editor}>
<FixedToolbar className="flex justify-start gap-1 rounded-t-lg">
<ToolbarButton onClick={() => editor.tf.h1.toggle()}>H1</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h2.toggle()}>H2</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h3.toggle()}>H3</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.blockquote.toggle()}>
Quote
</ToolbarButton>
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">
B
</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">
I
</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">
U
</MarkToolbarButton>
</FixedToolbar>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}
组件注册
注意我们如何使用 Plugin.withComponent(Component) 将组件注册到各自的插件。这是将 React 组件与 Plate 插件关联的推荐方法。
如需快速开始并预配置常用插件和组件,请使用 editor-basic 区块:
pnpm dlx shadcn@latest add @plate/editor-basic
这将为您处理大部分样板代码。
处理编辑器值
为了使编辑器内容持久化,让我们集成 localStorage 来在客户端保存和加载编辑器的值。
app/editor/page.tsx
'use client';
import * as React from 'react';
import type { Value } from 'platejs';
import {
BlockquotePlugin,
BoldPlugin,
H1Plugin,
H2Plugin,
H3Plugin,
ItalicPlugin,
UnderlinePlugin,
} from '@platejs/basic-nodes/react';
import {
Plate,
usePlateEditor,
} from 'platejs/react';
import { BlockquoteElement } from '@/components/ui/blockquote-node';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
import { H1Element, H2Element, H3Element } from '@/components/ui/heading-node';
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
import { ToolbarButton } from '@/components/ui/toolbar';
const initialValue: Value = [
{
children: [{ text: 'Title' }],
type: 'h3',
},
{
children: [{ text: 'This is a quote.' }],
type: 'blockquote',
},
{
children: [
{ text: 'With some ' },
{ bold: true, text: 'bold' },
{ text: ' text for emphasis!' },
],
type: 'p',
},
];
export default function MyEditorPage() {
const editor = usePlateEditor({
plugins: [
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
H1Plugin.withComponent(H1Element),
H2Plugin.withComponent(H2Element),
H3Plugin.withComponent(H3Element),
BlockquotePlugin.withComponent(BlockquoteElement),
],
value: () => {
const savedValue = localStorage.getItem('installation-next-demo');
return savedValue ? JSON.parse(savedValue) : initialValue;
},
});
return (
<Plate
editor={editor}
onChange={({ value }) => {
localStorage.setItem('installation-next-demo', JSON.stringify(value));
}}
>
<FixedToolbar className="flex justify-start gap-1 rounded-t-lg">
<ToolbarButton onClick={() => editor.tf.h1.toggle()}>H1</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h2.toggle()}>H2</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h3.toggle()}>H3</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.blockquote.toggle()}>Quote</ToolbarButton>
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">B</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">I</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">U</MarkToolbarButton>
<div className="flex-1" />
<ToolbarButton
className="px-2"
onClick={() => editor.tf.setValue(initialValue)}
>
Reset
</ToolbarButton>
</FixedToolbar>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}Loading...
Files
components/installation-next-04-value-demo.tsx
'use client';
import * as React from 'react';
import type { Value } from 'platejs';
import {
BlockquotePlugin,
BoldPlugin,
H1Plugin,
H2Plugin,
H3Plugin,
ItalicPlugin,
UnderlinePlugin,
} from '@platejs/basic-nodes/react';
import { Plate, usePlateEditor } from 'platejs/react';
import { BlockquoteElement } from '@/components/ui/blockquote-node';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { FixedToolbar } from '@/components/ui/fixed-toolbar';
import { H1Element, H2Element, H3Element } from '@/components/ui/heading-node';
import { MarkToolbarButton } from '@/components/ui/mark-toolbar-button';
import { ToolbarButton } from '@/components/ui/toolbar';
const initialValue: Value = [
{
children: [{ text: 'Title' }],
type: 'h3',
},
{
children: [{ text: 'This is a quote.' }],
type: 'blockquote',
},
{
children: [
{ text: 'With some ' },
{ bold: true, text: 'bold' },
{ text: ' text for emphasis!' },
],
type: 'p',
},
];
export default function MyEditorPage() {
const editor = usePlateEditor({
plugins: [
BoldPlugin,
ItalicPlugin,
UnderlinePlugin,
H1Plugin.withComponent(H1Element),
H2Plugin.withComponent(H2Element),
H3Plugin.withComponent(H3Element),
BlockquotePlugin.withComponent(BlockquoteElement),
],
value: () => {
const savedValue = localStorage.getItem(
`nextjs-plate-value-demo-${new Date().toISOString().split('T')[0]}`
);
return savedValue ? JSON.parse(savedValue) : initialValue;
},
});
return (
<Plate
onChange={({ value }) => {
localStorage.setItem(
`nextjs-plate-value-demo-${new Date().toISOString().split('T')[0]}`,
JSON.stringify(value)
);
}}
editor={editor}
>
<FixedToolbar className="flex justify-start gap-1 rounded-t-lg">
<ToolbarButton onClick={() => editor.tf.h1.toggle()}>H1</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h2.toggle()}>H2</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.h3.toggle()}>H3</ToolbarButton>
<ToolbarButton onClick={() => editor.tf.blockquote.toggle()}>
Quote
</ToolbarButton>
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">
B
</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">
I
</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">
U
</MarkToolbarButton>
<div className="flex-1" />
<ToolbarButton
className="px-2"
onClick={() => {
editor.tf.setValue(initialValue);
}}
>
Reset
</ToolbarButton>
</FixedToolbar>
<EditorContainer>
<Editor placeholder="Type your amazing content here..." />
</EditorContainer>
</Plate>
);
}
后续步骤
恭喜!您已在 Next.js 中构建了一个基础 Plate 编辑器。
要进一步增强您的编辑器: