Node.js

PreviousNext

Install and configure Plate for Node.js.

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.

Node.js Setup

Install Packages

Install the core runtime and the packages your pipeline needs.

pnpm add platejs @platejs/basic-nodes @platejs/markdown
pnpm add platejs @platejs/basic-nodes @platejs/markdown
PackageOwns
platejscreateBaseEditor, core editor APIs, core paragraph behavior.
@platejs/basic-nodesBase headings, blockquotes, horizontal rules, and text marks.
@platejs/markdownMarkdown serialization, deserialization, and the MarkdownPlugin API.

Create a Server Editor

Create the editor with base plugins only. The editor exposes editor.read(...) for committed state and editor.update(...) for writes without mounting a React tree.

scripts/process-content.ts
import type { Value } from 'platejs';
 
import {
  BaseBasicBlocksPlugin,
  BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import { createBaseEditor } from 'platejs';
 
const value: Value = [
  {
    children: [{ text: 'Document Title' }],
    type: 'h1',
  },
  {
    children: [
      { text: 'With ' },
      { bold: true, text: 'bold' },
      { text: ' text.' },
    ],
    type: 'p',
  },
];
 
const editor = createBaseEditor({
  plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin],
  value,
});
 
const plainText = editor.api.string([]);
 
console.info(plainText);
scripts/process-content.ts
import type { Value } from 'platejs';
 
import {
  BaseBasicBlocksPlugin,
  BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import { createBaseEditor } from 'platejs';
 
const value: Value = [
  {
    children: [{ text: 'Document Title' }],
    type: 'h1',
  },
  {
    children: [
      { text: 'With ' },
      { bold: true, text: 'bold' },
      { text: ' text.' },
    ],
    type: 'p',
  },
];
 
const editor = createBaseEditor({
  plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin],
  value,
});
 
const plainText = editor.api.string([]);
 
console.info(plainText);

Read and Write Markdown

Add MarkdownPlugin when the script needs Markdown helpers. Call deserializeMd and serializeMd directly for one-off conversion scripts.

scripts/markdown-io.ts
import {
  BaseBasicBlocksPlugin,
  BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import {
  MarkdownPlugin,
  deserializeMd,
  serializeMd,
} from '@platejs/markdown';
import { createBaseEditor } from 'platejs';
 
const editor = createBaseEditor({
  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);
scripts/markdown-io.ts
import {
  BaseBasicBlocksPlugin,
  BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import {
  MarkdownPlugin,
  deserializeMd,
  serializeMd,
} from '@platejs/markdown';
import { createBaseEditor } from 'platejs';
 
const editor = createBaseEditor({
  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);

Transform Content

Use transaction groups for migrations and bulk cleanup. Pass at: [] when the operation should scan the whole document.

scripts/normalize-headings.ts
import type { Value } from 'platejs';
 
import {
  BaseBasicBlocksPlugin,
  BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import { MarkdownPlugin, serializeMd } from '@platejs/markdown';
import { createBaseEditor } from 'platejs';
 
export function normalizeHeadings(value: Value) {
  const editor = createBaseEditor({
    plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin, MarkdownPlugin],
    value,
  });
 
  const insertAt = editor.read((state) => [state.value.root().length]);
 
  editor.update((tx) => {
    tx.nodes.set(
      { type: 'h2' },
      {
        at: [],
        match: (node) => 'type' in node && node.type === 'h1',
      }
    );
 
    tx.nodes.insert(
      [{ children: [{ text: 'Imported from the legacy CMS.' }], type: 'p' }],
      { at: insertAt }
    );
  });
 
  return {
    markdown: serializeMd(editor),
    text: editor.read((state) => state.text.string([])),
    value: editor.read((state) => state.value.root()),
  };
}
scripts/normalize-headings.ts
import type { Value } from 'platejs';
 
import {
  BaseBasicBlocksPlugin,
  BaseBasicMarksPlugin,
} from '@platejs/basic-nodes';
import { MarkdownPlugin, serializeMd } from '@platejs/markdown';
import { createBaseEditor } from 'platejs';
 
export function normalizeHeadings(value: Value) {
  const editor = createBaseEditor({
    plugins: [BaseBasicBlocksPlugin, BaseBasicMarksPlugin, MarkdownPlugin],
    value,
  });
 
  const insertAt = editor.read((state) => [state.value.root().length]);
 
  editor.update((tx) => {
    tx.nodes.set(
      { type: 'h2' },
      {
        at: [],
        match: (node) => 'type' in node && node.type === 'h1',
      }
    );
 
    tx.nodes.insert(
      [{ children: [{ text: 'Imported from the legacy CMS.' }], type: 'p' }],
      { at: insertAt }
    );
  });
 
  return {
    markdown: serializeMd(editor),
    text: editor.read((state) => state.text.string([])),
    value: editor.read((state) => state.value.root()),
  };
}

Runtime Boundaries

RuntimeImport fromUse for
Node.js scriptsplatejs, @platejs/*Migration, validation, serialization, search indexing.
React editorsplatejs/react, @platejs/*/reactEditable UI, hooks, rendered components, toolbar behavior.
Static renderingplatejs/staticServer-rendered read-only content.

API Reference

APIPackageNotes
createBaseEditorplatejsCreates a non-React editor instance.
editor.read((state) => state.text.string([]))platejsReads text from the whole document.
editor.update((tx) => tx.nodes.set(...))platejsUpdates matching nodes. Use at: [] for document-wide transforms.
editor.update((tx) => tx.nodes.insert(...))platejsInserts nodes at a path.
deserializeMd@platejs/markdownConverts Markdown into a Plate value.
serializeMd@platejs/markdownConverts the editor value or an explicit value option to Markdown.

Next Steps

TaskGuide
Serialize to MarkdownMarkdown
Serialize to HTMLHTML
Render read-only contentStatic Rendering
Query editor stateEditor API
Apply transformsEditor Transforms

Done. You now have a server-safe Plate runtime that can power migration scripts, validation jobs, and content serialization.