Skip to main content

@lexical/extension/BlockIdExtension

Interfaces​

BlockHandleOptions​

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:296

Experimental

Options of $resolveBlockHandle.

Properties​

read?​

optional read?: ReadonlyMap<string, string>

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:306

Experimental

The handles the model was shown, as $getBlockHandles returned them at the read (id → handle). With it, a handle names the block it named in that read, and is found only while that block still exists: not-found once it is deleted, even if another block's id now starts with the handle, and never ambiguous because of a block added since. Mistyped handles fail against the read's ids. Pass it whenever the document can change between the read and the edit.


BlockIdConfig​

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:413

Experimental

Properties​

$isIdentifiedBlock​

$isIdentifiedBlock: (node) => boolean

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:428

Experimental

Which nodes get an id. Evaluated when a node is marked dirty, so it should depend only on the node and its ancestors. Default: $defaultIsIdentifiedBlock (the root's children, nested blocks that hold text, and nested block decorators). Every reader of a document stored without ids must use the same predicate, since positional ids follow from it.

Parameters​
node​

LexicalNode

Returns​

boolean

disabled​

disabled: boolean

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:419

Experimental

Set to true to stop assigning, repairing, and indexing ids. Ids already stored on nodes are kept. While disabled, $getBlockById falls back to a scan of the document.

generateId​

generateId: () => string

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:440

Experimental

Makes a new id. It must return 1 to 64 characters of the base64url alphabet (A–Z, a–z, 0–9, -, _). A value that is invalid or already used in this editor is discarded and generateId is called again; 32 such values in a row throw. Ids made concurrently by independent writers cannot see each other, so a replacement must stay unique across writers (random ids of 120 bits or more, or a per-writer prefix). Tests can inject a deterministic counter. Default: generateBlockId.

Returns​

string

repairTransforms​

repairTransforms: boolean

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:452

Experimental

Whether the follow-up update that names the blocks an update applied with skipTransforms, or tagged COLLABORATION_TAG or HISTORIC_TAG, left without a valid id or with a repeated one runs the editor's transforms. With true (the default) it marks those blocks dirty, so every transform runs on them, and NodeStructureExtension, for example, repairs their structure in it. With false it sets their ids without running transforms, so it changes nothing else: for code that applies such updates and repairs or otherwise processes what they bring in an update of its own.


BlockIdOutput​

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:460

Experimental

The output of BlockIdExtension.

Properties​

$getBlockById​

$getBlockById: (id) => LexicalNode | null

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:464

Experimental

The same as $getBlockById for this editor.

Parameters​
id​

string

Returns​

LexicalNode | null

$getBlockHandle​

$getBlockHandle: (node) => string | null

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:466

Experimental

The same as $getBlockHandle for this editor.

Parameters​
node​

LexicalNode

Returns​

string | null

$getBlockHandles​

$getBlockHandles: () => Map<string, string>

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:468

Experimental

The same as $getBlockHandles for this editor.

Returns​

Map<string, string>

$isIdentifiedBlock​

$isIdentifiedBlock: (node) => boolean

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:475

Experimental

The configured BlockIdConfig.$isIdentifiedBlock.

Parameters​
node​

LexicalNode

Returns​

boolean

$resolveBlockHandle​

$resolveBlockHandle: (handle, options?) => BlockHandleResult

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:470

Experimental

The same as $resolveBlockHandle for this editor.

Parameters​
handle​

string

options?​

BlockHandleOptions

Returns​

BlockHandleResult

disabled​

disabled: Signal<boolean>

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:462

Experimental

See BlockIdConfig.disabled.

Type Aliases​

BlockHandleResult​

BlockHandleResult = { node: LexicalNode; status: "found"; } | { handle: string; status: "not-found"; } | { candidates: readonly string[]; handle: string; status: "ambiguous"; } | { handle: string; reason: "too-short" | "malformed"; status: "invalid"; }

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:193

Experimental

What $resolveBlockHandle found for a handle:

  • found: the one identified block that the handle names.
  • not-found: no identified block's id starts with it (a mistyped handle, or a block deleted since the handle was read).
  • ambiguous: several do (a block whose id shares the prefix was added since the handle was read, or, with read, a handle shorter than the read's). candidates holds their handles (current ones, or with read the read's), in ascending order of id, at most 10.
  • invalid: neither a handle nor an id. reason is 'too-short' for fewer than MIN_BLOCK_HANDLE_LENGTH characters that are not a whole id (a handle is never that short, so the model dropped a character), and 'malformed' for anything that is not 1 to 64 characters of the base64url alphabet (or not a string).

Variables​

BlockIdExtension​

const BlockIdExtension: LexicalExtension<BlockIdConfig, "@lexical/extension/BlockId", BlockIdOutput, unknown>

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:1808

Experimental

Gives blocks stable ids, stored as NodeState (blockIdState), so that code outside the editor (an AI agent, a server pipeline, a comment system) can address a block by id across sessions, replicas, and serializations.

Every identified block gets an id in the update that creates it. A document stored without ids gets positional ids, which every reader and replica derives alike; blocks created by edits get random ids (generateBlockId). Ids are unique: a copy yields to the block it was copied from. By default the root's children, nested blocks that hold text, and nested block decorators are identified ($defaultIsIdentifiedBlock). While enabled, $getBlockById looks blocks up in an index maintained from the dirty nodes of each update. See the Block IDs documentation page for the exact rules.

Showing blocks to a model? Use handles, the shortest unique prefix of each id (4 characters or more), instead of whole ids: $getBlockHandle / $resolveBlockHandle, or export: 'handle' in BlockIdHTMLExtension. A mistyped or outdated handle almost always resolves as not found or ambiguous rather than as another block.


blockIdState​

const blockIdState: StateConfig<"blockId", string | undefined>

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:112

Experimental

The NodeState that stores a block's id, serialized in node JSON as "$": {"blockId": "..."}.

It is ordinary NodeState, so it survives clones and undo, round-trips through exportJSON/importJSON, and is carried by anything that copies NodeState. It is reset by $copyNode, so a node split off another one (e.g. the new ListItemNode that Enter creates) starts without an id instead of with a duplicate.

A valid id is 1 to 64 characters of the base64url alphabet (A–Z, a–z, 0–9, -, _). Anything else reads as "no id", so the block gets a new one as if it had none.

Functions​

$defaultIsIdentifiedBlock()​

$defaultIsIdentifiedBlock(node): boolean

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:364

Experimental

The default BlockIdConfig.$isIdentifiedBlock:

  • every element and decorator child of the root ($isRootChildBlock), so a top-level list or table has an id too;
  • every nested block that holds text: a non-inline element whose class declares children: 'inline' in $config() (or a list that includes 'inline'), such as a paragraph, heading, quote or code block in a table cell, or a list item;
  • every nested non-inline decorator.

Nested wrappers get no id: nested lists, table rows and cells, shadow roots (a shadow root quote holds blocks, whatever its class declares), and nested elements whose class declares no children. A slot is nested like a shadow root, so blocks in slots follow the same rules. Compose it to identify more:

configExtension(BlockIdExtension, {
$isIdentifiedBlock: (node) =>
$defaultIsIdentifiedBlock(node) || $isTableCellNode(node),
});

Parameters​

node​

LexicalNode

Returns​

boolean


$getBlockById()​

$getBlockById(id): LexicalNode | null

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:171

Experimental

Returns the identified block with the given id in the active editor state, or null. Requires BlockIdExtension.

The lookup goes through an index kept up to date from the dirty nodes of each commit and is verified against the active state, so it never returns a stale node. A block created, replaced or re-keyed earlier in the same update is found once that update has committed: after $getBlockById(id).replace(node), look the id up again in a later update. The match is exact: a prefix of an id finds nothing.

Showing blocks to a model? Use handles, which cost a few tokens instead of a whole id: $getBlockHandle / $resolveBlockHandle, or export: 'handle' in BlockIdHTMLExtension.

// What the model reads, and the handles it is shown.
const [blocks, shown] = editor.read(() => [
$getRoot()
.getChildren()
.map(node => ({block: $getBlockHandle(node), text: node.getTextContent()})),
$getBlockHandles(),
]);
// What it answers: {block: 'V1St', text: 'Q4 plan'}
editor.update(() => {
const result = $resolveBlockHandle(op.block, {read: shown});
if (result.status === 'found') {
// Change the block in place, only the characters that differ (the
// Block IDs page has a `$setBlockText` that does): replace() would
// re-create it, and what a collaborator types in it meanwhile would
// not stay where they typed it.
$setBlockText(result.node, op.text);
}
});

Parameters​

id​

string

Returns​

LexicalNode | null


$getBlockHandle()​

$getBlockHandle(node): string | null

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:232

Experimental

Returns the handle of an identified block: the shortest prefix of its id, at least MIN_BLOCK_HANDLE_LENGTH characters long, that no other identified block's id starts with, like a short git hash (V1St for V1StGXR8_Z5jdHi6B-myT). An id shorter than that, or one that another id starts with, is its own handle. Returns null for a node that has no id or that $getBlockById would not find by its id (one that is not attached or not an identified block, a block named since the last commit, or a copy whose repeated id awaits its repair). Requires BlockIdExtension.

Handles are for showing blocks to a model at a fraction of the tokens of a whole id: $resolveBlockHandle takes them back. They are derived from the ids of the document, never stored, so a handle can grow longer when a block whose id shares its prefix is added. A handle read earlier then resolves as ambiguous rather than to either block, unless the new block's whole id is the handle (an exact id always wins).

Handles are computed from the same blocks as $getBlockById: those of the last committed state while the extension is enabled (all of them at once, then cached until the next commit changes an id), and a scan of the document on each call while it is disabled.

Parameters​

node​

LexicalNode

Returns​

string | null


$getBlockHandles()​

$getBlockHandles(): Map<string, string>

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:243

Experimental

Returns the handle of every identified block, by id (see $getBlockHandle). The map is a new one on each call. Requires BlockIdExtension.

Returns​

Map<string, string>


$getBlockId()​

$getBlockId(node): string | null

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:127

Experimental

Returns the id stored on node, or null if it has none.

This reads the NodeState directly, so it also returns an id left on a node that is not an identified block (a top-level list that indenting nested into a list item, say); such ids are not indexed and $getBlockById does not find them.

Parameters​

node​

LexicalNode

Returns​

string | null


$isRootChildBlock()​

$isRootChildBlock(node): boolean

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:320

Experimental

A BlockIdConfig.$isIdentifiedBlock that identifies every element and decorator child of the root, and nothing nested. Use it when only top-level blocks are addressed:

configExtension(BlockIdExtension, {$isIdentifiedBlock: $isRootChildBlock});

Parameters​

node​

LexicalNode

Returns​

boolean


$resolveBlockHandle()​

$resolveBlockHandle(handle, options?): BlockHandleResult

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:281

Experimental

Finds the identified block that a handle from $getBlockHandle names, for input that cannot be trusted (a language model's answer): it reports a mistyped, outdated or invalid handle in a BlockHandleResult instead of throwing. It returns a block only when exactly one block's id starts with the handle (or equals it). Requires BlockIdExtension.

A whole id is accepted too, and an exact match wins (b1 finds the block b1 even when b12 exists). Otherwise the handle must be at least MIN_BLOCK_HANDLE_LENGTH characters long (a shorter one is invalid, reason 'too-short') and the start of exactly one block's id. A handle that was longer when it was read (another id shared its prefix then) still finds its block.

Without options, a handle names whichever block's id starts with it now, not the block it was read from: if that block was deleted and another block's id starts with the same prefix (a block added since, or a block whose id the deleted one's prefixed), it names that block. Whenever the document can change between the read and the edit, pass the handles the model was shown as BlockHandleOptions.read: the handle then names the block it named in the read, found while that block still exists and not-found once it is deleted.

Resolve against the latest state, so that deletions since the read count: in the update that applies the edit in a live editor; with @lexical/y on a server, in a readYState of the stored state, before the edit is applied by id to the state the model read and merged (see "Showing blocks to a model" in the Block IDs documentation). Like $getBlockById, a block created earlier in the same update is found once that update has committed.

Parameters​

handle​

string

options?​

BlockHandleOptions

Returns​

BlockHandleResult


generateBlockId()​

generateBlockId(): string

Defined in: packages/lexical-extension/src/BlockIdExtension.ts:396

Experimental

The default BlockIdConfig.generateId: 21 characters of the base64url alphabet (126 random bits, more than the 122 of a version 4 UUID), from crypto.getRandomValues. Ids need no coordination between independent writers (replicas, servers, agents): ids made independently practically never collide, and a mistyped id (from a language model, say) almost never names another block.

There is no weaker fallback: where crypto.getRandomValues is missing (React Native without a polyfill, for instance), it throws, and the editor must be given a generateId of its own.

Returns​

string

References​

MIN_BLOCK_HANDLE_LENGTH​

Re-exports MIN_BLOCK_HANDLE_LENGTH