Skip to main content

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).
listType
ListType
The type of list: 'number' | 'bullet' | 'check'
start
number
Starting number for ordered lists (default: 1)

ListItemNode

Represents an individual item within a list.
value
number
The item number in ordered lists
checked
boolean | undefined
Whether the checkbox is checked (for check lists)

Factory Functions

$createListNode

Creates a new ListNode.
function $createListNode(listType: ListType, start?: number): ListNode
listType
ListType
required
The type of list: 'number', 'bullet', or 'check'
start
number
default:"1"
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
checked
boolean
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
options.restoreNumbering
boolean
Whether to restore numbering when splitting lists

registerListStrictIndentTransform

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
    })
  ]
});
hasStrictIndent
boolean
default:"false"
When true, enforces strict indentation rules for list items
shouldPreserveNumbering
boolean
default:"false"
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
    })
  ]
});
disableTakeFocusOnClick
boolean
default:"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);

Build docs developers (and LLMs) love