Overview
The @lexical/table package provides comprehensive table functionality for Lexical, including table creation, manipulation, cell merging, and table-specific selection.
Installation
npm install @lexical/table
Nodes
TableNode
Represents a table element.
Methods:
Returns the column widths array
TableRowNode
Represents a table row (<tr>).
TableCellNode
Represents a table cell (<td> or <th>).
Cell header state: NO_STATUS, ROW, COLUMN, or BOTH
Number of columns the cell spans (default: 1)
Number of rows the cell spans (default: 1)
Factory Functions
$createTableNode
Creates a new TableNode.
function $createTableNode(): TableNode
$createTableRowNode
Creates a new TableRowNode.
function $createTableRowNode(height?: number): TableRowNode
$createTableCellNode
Creates a new TableCellNode.
function $createTableCellNode(
headerState: TableCellHeaderStates,
colSpan?: number,
width?: number
): TableCellNode
Example:
import {
$createTableNode,
$createTableRowNode,
$createTableCellNode,
TableCellHeaderStates
} from '@lexical/table';
editor.update(() => {
const table = $createTableNode();
// Create header row
const headerRow = $createTableRowNode();
const headerCell1 = $createTableCellNode(TableCellHeaderStates.ROW);
headerCell1.append($createParagraphNode().append($createTextNode('Header 1')));
const headerCell2 = $createTableCellNode(TableCellHeaderStates.ROW);
headerCell2.append($createParagraphNode().append($createTextNode('Header 2')));
headerRow.append(headerCell1, headerCell2);
// Create data row
const dataRow = $createTableRowNode();
const dataCell1 = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
dataCell1.append($createParagraphNode().append($createTextNode('Data 1')));
const dataCell2 = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
dataCell2.append($createParagraphNode().append($createTextNode('Data 2')));
dataRow.append(dataCell1, dataCell2);
table.append(headerRow, dataRow);
$getRoot().append(table);
});
$createTableNodeWithDimensions
Creates a table with specified dimensions.
function $createTableNodeWithDimensions(
rowCount: number,
columnCount: number,
includeHeaders?: InsertTableCommandPayloadHeaders
): TableNode
Number of columns to create
Whether to include header rows/columns: true, false, 'rows', or 'columns'
Type Guards
$isTableNode
function $isTableNode(node: LexicalNode | null | undefined): node is TableNode
$isTableRowNode
function $isTableRowNode(node: LexicalNode | null | undefined): node is TableRowNode
$isTableCellNode
function $isTableCellNode(node: LexicalNode | null | undefined): node is TableCellNode
$isTableSelection
function $isTableSelection(selection: BaseSelection | null): selection is TableSelection
Commands
INSERT_TABLE_COMMAND
Inserts a new table at the current selection.
const INSERT_TABLE_COMMAND: LexicalCommand<InsertTableCommandPayload>
Payload:
Example:
editor.dispatchCommand(INSERT_TABLE_COMMAND, {
rows: 3,
columns: 3,
includeHeaders: 'rows'
});
Table Manipulation Utilities
$insertTableRow
Inserts a row in a table.
function $insertTableRow(
tableNode: TableNode,
targetIndex: number,
shouldInsertAfter?: boolean,
rowCount?: number,
grid?: TableMapType
): void
$insertTableColumn
Inserts a column in a table.
function $insertTableColumn(
tableNode: TableNode,
targetIndex: number,
shouldInsertAfter?: boolean,
columnCount?: number,
grid?: TableMapType
): void
$deleteTableRow
Deletes a table row.
function $deleteTableRowAtSelection(): void
$deleteTableColumn
Deletes a table column.
function $deleteTableColumnAtSelection(): void
$mergeCells
Merges selected table cells.
function $mergeCells(): void
$unmergeCell
Unmerges a merged table cell.
function $unmergeCell(): void
$computeTableMap
Computes the table map for a given table.
function $computeTableMap(
table: TableNode,
cellA: TableCellNode,
cellB: TableCellNode
): [TableMapType, TableMapValueType, TableMapValueType]
Selection Utilities
$createTableSelection
Creates a new table selection.
function $createTableSelection(): TableSelection
$findCellNode
Finds the table cell node from any node.
function $findCellNode(node: LexicalNode): TableCellNode | null
$findTableNode
Finds the table node from any node.
function $findTableNode(node: LexicalNode): TableNode | null
$getTableCellNodeRect
Gets the row and column indices of a cell.
function $getTableCellNodeRect(cell: TableCellNode): {
rowIndex: number;
columnIndex: number;
rowSpan: number;
colSpan: number;
} | null
Registration
registerTablePlugin
Registers core table functionality.
function registerTablePlugin(
editor: LexicalEditor,
config: TableConfig
): () => void
registerTableSelectionObserver
Registers table selection observer.
function registerTableSelectionObserver(
editor: LexicalEditor,
hasTabHandler: boolean
): () => void
Registers transform to handle cell unmerging.
function registerTableCellUnmergeTransform(editor: LexicalEditor): () => void
Extension
TableExtension
Bundles all table functionality.
import { createEditor } from 'lexical';
import { TableExtension } from '@lexical/table';
const editor = createEditor({
extensions: [
TableExtension.configure({
hasTabHandler: true,
hasHorizontalScroll: false
})
]
});
Enable Tab key navigation between cells
Enable horizontal scrolling for wide tables
Table Observer
TableObserver
Observes and manages table DOM state.
class TableObserver {
constructor(editor: LexicalEditor, tableNodeKey: NodeKey);
removeListeners(): void;
}
$getTableAndElementByKey
Gets table node and element by key.
function $getTableAndElementByKey(
nodeKey: NodeKey
): [TableNode, HTMLTableElement] | [null, null]
Types
enum TableCellHeaderStates {
NO_STATUS = 0,
ROW = 1,
COLUMN = 2,
BOTH = 3
}
TableSelection
Custom selection type for table cells.
InsertTableCommandPayload
type InsertTableCommandPayload = {
columns: number;
rows: number;
includeHeaders?: InsertTableCommandPayloadHeaders;
}
type InsertTableCommandPayloadHeaders = true | false | 'rows' | 'columns'
Complete Example
import { createEditor } from 'lexical';
import {
TableExtension,
$createTableNodeWithDimensions,
$insertTableRow,
$insertTableColumn,
INSERT_TABLE_COMMAND
} from '@lexical/table';
const editor = createEditor({
extensions: [
TableExtension.configure({
hasTabHandler: true
})
]
});
editor.setRootElement(document.getElementById('editor'));
// Insert table via command
editor.dispatchCommand(INSERT_TABLE_COMMAND, {
rows: 4,
columns: 3,
includeHeaders: 'rows'
});
// Or create programmatically
editor.update(() => {
const table = $createTableNodeWithDimensions(3, 3, true);
$getRoot().append(table);
// Add a row
$insertTableRow(table, 1, true);
// Add a column
$insertTableColumn(table, 2, true);
});