Node ID plugin for Plate rich-text editor that automatically assigns unique identifiers to nodes
npx @tessl/cli install tessl/npm-udecode--plate-node-id@49.0.0The Plate Node ID Plugin automatically assigns unique identifiers to nodes in a Plate rich-text editor. It enables features like drag & drop by ensuring each node has a unique ID, with configurable options for ID generation, filtering, and handling of duplicate IDs.
npm install @udecode/plate-node-idimport { NodeIdPlugin, type NodeIdConfig, withNodeId } from "@udecode/plate-node-id";For CommonJS:
const { NodeIdPlugin, withNodeId } = require("@udecode/plate-node-id");import { createPlateEditor } from "@udecode/plate";
import { NodeIdPlugin } from "@udecode/plate-node-id";
// Add the plugin to your Plate editor
const editor = createPlateEditor({
plugins: [
NodeIdPlugin,
// ... other plugins
],
});
// The plugin automatically assigns IDs to nodes as they're inserted
const nodeWithId = {
type: "paragraph",
children: [{ text: "Hello world" }],
// id: "abc123" - automatically added by the plugin
};The Node ID Plugin follows Plate's plugin architecture with three main components:
NodeIdPlugin - Main plugin configuration that defines default options and normalization behaviorwithNodeId - Transforms editor behavior to handle automatic ID assignment during insert operationsNodeIdConfig - Comprehensive options for controlling ID generation, filtering, and deduplication logicThe plugin integrates with Slate.js transforms to intercept insert operations and automatically assign unique IDs while handling edge cases like undo/redo, copy/paste, and node splitting.
Main plugin instance that provides Node ID functionality to Plate editor.
const NodeIdPlugin: SlatePlugin<NodeIdConfig>;Editor override function that enables Node ID functionality by intercepting editor transforms.
const withNodeId: OverrideEditor<NodeIdConfig>;type NodeIdConfig = PluginConfig<
'nodeId',
{
disableInsertOverrides?: boolean;
filterInline?: boolean;
filterText?: boolean;
idKey?: string;
normalizeInitialValue?: boolean;
reuseId?: boolean;
idCreator?: () => any;
} & QueryNodeOptions
>;Configuration Properties:
disableInsertOverrides?: boolean - By default, when a node inserted using editor.tf.insertNode(s) has an id, it will be used instead of the id generator, except if it already exists in the document. Set this option to true to disable this behavior. (default: undefined)filterInline?: boolean - Filter inline Element nodes. (default: true)filterText?: boolean - Filter Text nodes. (default: true)idKey?: string - Node key to store the id. (default: 'id')normalizeInitialValue?: boolean - Normalize initial value. If false, normalize only the first and last node are missing id. To disable this behavior, use NodeIdPlugin.configure({ normalizeInitialValue: null }). (default: false)reuseId?: boolean - Reuse ids on undo/redo and copy/pasting if not existing in the document. This is disabled by default to avoid duplicate ids across documents. (default: false)idCreator?: () => any - A function that generates and returns a unique ID. (default: () => nanoid(10))Inherited from QueryNodeOptions:
allow?: QueryNodeOptions['allow'] - Node types to allowexclude?: QueryNodeOptions['exclude'] - Node types to excludefilter?: QueryNodeOptions['filter'] - Custom filter function for nodesimport { NodeIdPlugin } from "@udecode/plate-node-id";
import { v4 as uuidv4 } from "uuid";
const editor = createPlateEditor({
plugins: [
NodeIdPlugin.configure({
options: {
idCreator: () => uuidv4(), // Use UUID instead of nanoid
},
}),
],
});import { NodeIdPlugin } from "@udecode/plate-node-id";
const editor = createPlateEditor({
plugins: [
NodeIdPlugin.configure({
options: {
idKey: "nodeId", // Store IDs in 'nodeId' instead of 'id'
},
}),
],
});import { NodeIdPlugin } from "@udecode/plate-node-id";
const editor = createPlateEditor({
plugins: [
NodeIdPlugin.configure({
options: {
filterText: false, // Assign IDs to text nodes as well
},
}),
],
});import { NodeIdPlugin } from "@udecode/plate-node-id";
const editor = createPlateEditor({
plugins: [
NodeIdPlugin.configure({
options: {
reuseId: true, // Reuse existing IDs if not duplicated
},
}),
],
});import { NodeIdPlugin } from "@udecode/plate-node-id";
const editor = createPlateEditor({
plugins: [
NodeIdPlugin.configure({
options: {
normalizeInitialValue: true, // Add IDs to all nodes on initialization
},
}),
],
value: [
{
type: "paragraph",
children: [{ text: "This will get an ID" }],
// No id property - will be automatically added
},
],
});Note: Types like PluginConfig, QueryNodeOptions, and NodeEntry are re-exported from @udecode/plate for convenience, but originally come from various Plate core packages.
// Core plugin type (derived from createTSlatePlugin return type)
type SlatePlugin<C extends AnyPluginConfig = PluginConfig> = {
key: string;
options: C;
// ... other plugin properties
};
type OverrideEditor<TConfig = PluginConfig> = (params: {
editor: PlateEditor;
getOptions: () => TConfig;
tf: EditorTransforms;
}) => Partial<PlateEditor>;
type PluginConfig<TKey = string, TOptions = {}> = {
key: TKey;
} & TOptions;
interface QueryNodeOptions {
allow?: string[] | string;
exclude?: string[] | string;
filter?: (nodeEntry: NodeEntry) => boolean;
}// Re-exported from @udecode/plate
type Descendant = Element | Text;
type NodeEntry<T = Node> = [T, Path];
interface NodeProps<T = {}> extends T {
[key: string]: unknown;
}
type TNode = Element | Text;