Overview
The @lexical/list package provides comprehensive list functionality for Lexical, supporting ordered lists, unordered lists, and check lists with proper indentation and keyboard handling.
Installation
npm install @lexical/list
Nodes
ListNode
Represents a list container (ol, ul, or checklist).
The type of list: 'number' | 'bullet' | 'check'
Starting number for ordered lists (default: 1)
ListItemNode
Represents an individual item within a list.
The item number in ordered lists
Whether the checkbox is checked (for check lists)
Factory Functions
$createListNode
Creates a new ListNode.
function $createListNode(listType: ListType, start?: number): ListNode
The type of list: 'number', 'bullet', or 'check'
Starting number for ordered lists
Example:
import { $createListNode, $createListItemNode } from '@lexical/list';
editor.update(() => {
const list = $createListNode('bullet');
const item1 = $createListItemNode();
item1.append($createTextNode('First item'));
const item2 = $createListItemNode();
item2.append($createTextNode('Second item'));
list.append(item1, item2);
$getRoot().append(list);
});
$createListItemNode
Creates a new ListItemNode.
function $createListItemNode(checked?: boolean): ListItemNode
Initial checked state for check list items
Type Guards
$isListNode
function $isListNode(node: LexicalNode | null | undefined): node is ListNode
$isListItemNode
function $isListItemNode(node: LexicalNode | null | undefined): node is ListItemNode
Commands
INSERT_ORDERED_LIST_COMMAND
Inserts or converts selection to an ordered list.
const INSERT_ORDERED_LIST_COMMAND: LexicalCommand<void>
Example:
editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
INSERT_UNORDERED_LIST_COMMAND
Inserts or converts selection to an unordered (bullet) list.
const INSERT_UNORDERED_LIST_COMMAND: LexicalCommand<void>
INSERT_CHECK_LIST_COMMAND
Inserts or converts selection to a check list.
const INSERT_CHECK_LIST_COMMAND: LexicalCommand<void>
REMOVE_LIST_COMMAND
Removes list formatting from the selection.
const REMOVE_LIST_COMMAND: LexicalCommand<void>
UPDATE_LIST_START_COMMAND
Updates the starting number of an ordered list.
const UPDATE_LIST_START_COMMAND: LexicalCommand<{
listNodeKey: NodeKey;
newStart: number;
}>
Utilities
$insertList
Inserts a list at the current selection.
function $insertList(listType: ListType): void
$removeList
Removes list formatting from the current selection.
function $removeList(): void
$getListDepth
Gets the nesting depth of a list node.
function $getListDepth(listNode: ListNode): number
$handleListInsertParagraph
Handles paragraph insertion within lists (called on Enter key).
function $handleListInsertParagraph(shouldRestore: boolean): boolean
Registration
registerList
Registers list functionality including commands and transforms.
function registerList(
editor: LexicalEditor,
options?: RegisterListOptions
): () => void
Whether to restore numbering when splitting lists
Registers a transform that enforces strict indentation rules.
function registerListStrictIndentTransform(editor: LexicalEditor): () => void
registerCheckList
Registers check list specific functionality.
function registerCheckList(editor: LexicalEditor, config: CheckListConfig): () => void
Extensions
ListExtension
Core list extension with ListNode and ListItemNode.
import { createEditor } from 'lexical';
import { ListExtension } from '@lexical/list';
const editor = createEditor({
extensions: [
ListExtension.configure({
hasStrictIndent: false,
shouldPreserveNumbering: false
})
]
});
When true, enforces strict indentation rules for list items
When true, preserves numbering when splitting ordered lists
CheckListExtension
Adds check list functionality.
import { ListExtension, CheckListExtension } from '@lexical/list';
const editor = createEditor({
extensions: [
ListExtension,
CheckListExtension.configure({
disableTakeFocusOnClick: false
})
]
});
When true, clicking checkboxes doesn’t focus the editor
Types
ListType
type ListType = 'number' | 'bullet' | 'check'
ListNodeTagType
type ListNodeTagType = 'ul' | 'ol'
SerializedListNode
type SerializedListNode = Spread<
{
listType: ListType;
start: number;
tag: ListNodeTagType;
},
SerializedElementNode
>
SerializedListItemNode
type SerializedListItemNode = Spread<
{
checked: boolean | undefined;
value: number;
},
SerializedElementNode
>
Complete Example
import { createEditor } from 'lexical';
import {
ListExtension,
CheckListExtension,
$createListNode,
$createListItemNode,
INSERT_ORDERED_LIST_COMMAND
} from '@lexical/list';
const editor = createEditor({
extensions: [
ListExtension.configure({
shouldPreserveNumbering: true
}),
CheckListExtension
]
});
editor.setRootElement(document.getElementById('editor'));
// Create a nested list
editor.update(() => {
const list = $createListNode('number');
const item1 = $createListItemNode();
item1.append($createTextNode('First item'));
const item2 = $createListItemNode();
item2.append($createTextNode('Second item with nested list'));
const nestedList = $createListNode('bullet');
const nestedItem = $createListItemNode();
nestedItem.append($createTextNode('Nested item'));
nestedList.append(nestedItem);
item2.append(nestedList);
list.append(item1, item2);
$getRoot().append(list);
});
// Or use command to convert selection to list
editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);