content/docs/installation/node.mdx
Use Plate in Node.js when you need to read, validate, transform, or serialize editor values outside the browser. Node scripts use the base runtime imports, while React editors use /react subpaths. This guide walks through a server-safe editor, Markdown IO, and a content transform.
Install the core runtime and the packages your pipeline needs.
npm install platejs @platejs/basic-nodes @platejs/markdown
| Package | Owns |
|---|---|
platejs | createSlateEditor, core editor APIs, core paragraph behavior. |
@platejs/basic-nodes | Base headings, blockquotes, horizontal rules, and text marks. |
@platejs/markdown | Markdown serialization, deserialization, and the MarkdownPlugin API. |
Create the editor with base plugins only. The editor exposes the same editor.api
and editor.tf surfaces without mounting a React tree.
import type { Value } from 'platejs';
import {
BaseBasicBlocksPlugin,
BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import { createSlateEditor } from 'platejs';
const value: Value = [
{
children: [{ text: 'Document Title' }],
type: 'h1',
},
{
children: [
{ text: 'With ' },
{ bold: true, text: 'bold' },
{ text: ' text.' },
],
type: 'p',
},
];
const editor = createSlateEditor({
plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin],
value,
});
const plainText = editor.api.string([]);
console.info(plainText);
Add MarkdownPlugin when the script needs Markdown helpers through
editor.getApi(MarkdownPlugin). You can also call deserializeMd and
serializeMd directly.
import {
BaseBasicBlocksPlugin,
BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import {
MarkdownPlugin,
deserializeMd,
serializeMd,
} from '@platejs/markdown';
import { createSlateEditor } from 'platejs';
const editor = createSlateEditor({
plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin, MarkdownPlugin],
});
const value = deserializeMd(
editor,
[
'# Migration Note',
'',
'Move legacy content into **Plate** format.',
].join('\n')
);
const markdown = serializeMd(editor, { value });
console.info(markdown);
Use transforms for migrations and bulk cleanup. Pass at: [] when the operation
should scan the whole document.
import type { Value } from 'platejs';
import {
BaseBasicBlocksPlugin,
BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import { MarkdownPlugin, serializeMd } from '@platejs/markdown';
import { createSlateEditor } from 'platejs';
export function normalizeHeadings(value: Value) {
const editor = createSlateEditor({
plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin, MarkdownPlugin],
value,
});
editor.tf.setNodes(
{ type: 'h2' },
{
at: [],
match: (node) => 'type' in node && node.type === 'h1',
}
);
editor.tf.insertNodes(
[{ children: [{ text: 'Imported from the legacy CMS.' }], type: 'p' }],
{ at: [editor.children.length] }
);
return {
markdown: serializeMd(editor),
text: editor.api.string([]),
value: editor.children,
};
}
| Runtime | Import from | Use for |
|---|---|---|
| Node.js scripts | platejs, @platejs/* | Migration, validation, serialization, search indexing. |
| React editors | platejs/react, @platejs/*/react | Editable UI, hooks, rendered components, toolbar behavior. |
| Static rendering | platejs/static | Server-rendered read-only content. |
| API | Package | Notes |
|---|---|---|
createSlateEditor | platejs | Creates a non-React editor instance. |
editor.api.string([]) | platejs | Reads text from the whole document. |
editor.tf.setNodes | platejs | Updates matching nodes. Use at: [] for document-wide transforms. |
editor.tf.insertNodes | platejs | Inserts nodes at a path. |
deserializeMd | @platejs/markdown | Converts Markdown into a Plate value. |
serializeMd | @platejs/markdown | Converts the editor value or an explicit value option to Markdown. |
| Task | Guide |
|---|---|
| Serialize to Markdown | Markdown |
| Serialize to HTML | HTML |
| Render read-only content | Static Rendering |
| Query editor state | Editor API |
| Apply transforms | Editor Transforms |
Done. You now have a server-safe Plate runtime that can power migration scripts, validation jobs, and content serialization.