@lexical/extension/BlockIdExtension
Interfaces
BlockHandleOptions
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:300
Experimental
Options of $resolveBlockHandle.
Properties
read?
optionalread?:ReadonlyMap<string,string>
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:310
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:417
Experimental
Properties
$isIdentifiedBlock
$isIdentifiedBlock: (
node) =>boolean
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:432
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
Returns
boolean
disabled
disabled:
boolean
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:423
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:449
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).
Handles ($getBlockHandle) hash the ids, so they stay short and typo-safe whatever the ids look like: a timestamp first (UUIDv7) or a counter works as well as random ids.
Tests can inject a deterministic counter. Default: generateBlockId.
Returns
string
repairTransforms
repairTransforms:
boolean
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:461
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:469
Experimental
The output of BlockIdExtension.
Properties
$getBlockById
$getBlockById: (
id) =>LexicalNode|null
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:473
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:475
Experimental
The same as $getBlockHandle for this editor.
Parameters
node
Returns
string | null
$getBlockHandles
$getBlockHandles: () =>
Map<string,string>
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:477
Experimental
The same as $getBlockHandles for this editor.
Returns
Map<string, string>
$isIdentifiedBlock
$isIdentifiedBlock: (
node) =>boolean
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:484
Experimental
The configured BlockIdConfig.$isIdentifiedBlock.
Parameters
node
Returns
boolean
$resolveBlockHandle
$resolveBlockHandle: (
handle,options?) =>BlockHandleResult
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:479
Experimental
The same as $resolveBlockHandle for this editor.
Parameters
handle
string
options?
Returns
disabled
disabled:
Signal<boolean>
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:471
Experimental
Type Aliases
BlockHandleResult
BlockHandleResult = {
node:LexicalNode;status:"found"; } | {handle:string;status:"not-found"; } | {candidates: readonlystring[];handle:string;status:"ambiguous"; } | {handle:string;reason:"too-short"|"malformed";status:"invalid"; }
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:198
Experimental
What $resolveBlockHandle found for a handle:
found: the one identified block that the handle names.not-found: it names no identified block (a mistyped handle, or a block deleted since the handle was read).ambiguous: it names several (a handle shorter than the one shown, or, withoutread, a block added since whose handle shares its characters).candidatesholds their handles (current ones, or withreadthe read's), at most 10.invalid: neither a handle nor an id.reasonis'too-short'for fewer than MIN_BLOCK_HANDLE_LENGTH characters (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
constBlockIdExtension:LexicalExtension<BlockIdConfig,"@lexical/extension/BlockId",BlockIdOutput,unknown>
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:1898
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, short opaque names derived from a
hash of each id (almost always 4 characters), instead of whole ids:
$getBlockHandle / $resolveBlockHandle, or
handles: true in BlockIdHTMLExtension. A mistyped or outdated
handle almost always resolves as not found or ambiguous rather than as
another block.
blockIdState
constblockIdState:StateConfig<"blockId",string|undefined>
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:116
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:368
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
Returns
boolean
$getBlockById()
$getBlockById(
id):LexicalNode|null
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:176
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
handles: true in BlockIdHTMLExtension.
// What the model reads, and the handles it is shown:
// [{block: '2bNo', text: 'Q3 plan'}, ...]
const [blocks, shown] = editor.read(() => [
$getRoot()
.getChildren()
.map(node => ({block: $getBlockHandle(node), text: node.getTextContent()})),
$getBlockHandles(),
]);
// What it answers: {block: '2bNo', 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:239
Experimental
Returns the handle of an identified block: a short, opaque name for it in
the current document, 4 to 11 characters of the base64url alphabet (2bNo
for V1StGXR8_Z5jdHi6B-myT). It is the shortest prefix, at least
MIN_BLOCK_HANDLE_LENGTH characters long, of a 64-bit hash of the
block's id that no other identified block's hash starts with, like a short
git hash. Hashing makes handles short (almost always 4 characters) and
typo-safe whatever the ids look like: random, a timestamp first (UUIDv7),
a counter, or @lexical/y's derived ids. 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 hash shares its characters is added; a handle read
earlier then resolves as ambiguous rather than to either block.
Handles are computed all at once from the blocks of the last committed state: from the index of $getBlockById while the extension is enabled (cached until the ids change), and from a scan of that state while it is disabled (cached until the next commit).
Parameters
node
Returns
string | null
$getBlockHandles()
$getBlockHandles():
Map<string,string>
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:250
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:131
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
Returns
string | null
$isRootChildBlock()
$isRootChildBlock(
node):boolean
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:324
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
Returns
boolean
$resolveBlockHandle()
$resolveBlockHandle(
handle,options?):BlockHandleResult
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:285
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 the handle names exactly one. Requires BlockIdExtension.
A handle has 4 to 11 characters (fewer is invalid, reason
'too-short'), and names the blocks whose hash it starts. A handle that
was longer when it was read still finds its block. A string longer than 11
characters is taken as a whole id instead, so a whole id of 12 characters
or more is accepted too; only handles are typo-safe.
Without options, a handle names whichever block it names now, not the
block it was read from: if that block was deleted and a block added since
has a hash that starts the same way, 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?
Returns
generateBlockId()
generateBlockId():
string
Defined in: packages/lexical-extension/src/BlockIdExtension.ts:400
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