List Keymap
ListKeymap provides keyboard shortcuts for manipulating list items: Tab to indent (sink), Shift-Tab to outdent (lift), and Backspace at the start of a list item to lift it out of the list. It works with bullet lists and ordered lists but does not interfere with task lists, which use their own item type.
Included in StarterKit by default.
ListKeymap is included in StarterKit, so it works out of the box. To use it standalone:
import { Editor, Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap,} from '@domternal/core';import '@domternal/theme';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap], content: '<ul><li>First item</li><li>Second item (press Tab to indent)</li></ul>',});import { Component, signal } from '@angular/core';import { DomternalEditorComponent } from '@domternal/angular';import { Editor, Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap,} from '@domternal/core';
@Component({ selector: 'app-editor', imports: [DomternalEditorComponent], templateUrl: './editor.html',})export class EditorComponent { editor = signal<Editor | null>(null); extensions = [Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap]; content = '<ul><li>First item</li><li>Second item (press Tab to indent)</li></ul>';}<domternal-editor [extensions]="extensions" [content]="content" (editorCreated)="editor.set($event)"/>import { Domternal } from '@domternal/react';import { Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap,} from '@domternal/core';
export default function Editor() { return ( <Domternal extensions={[Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap]} content="<ul><li>First item</li><li>Second item (press Tab to indent)</li></ul>" > <Domternal.Content /> </Domternal> );}import { Editor, Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap,} from '@domternal/core';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [Document, Paragraph, Text, BulletList, OrderedList, ListItem, ListKeymap], content: '<ul><li>First item</li><li>Second item (press Tab to indent)</li></ul>',});To configure ListKeymap in StarterKit:
StarterKit.configure({ listKeymap: { listItem: 'listItem' },})To disable ListKeymap in StarterKit:
StarterKit.configure({ listKeymap: false })Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
listItem | string | 'listItem' | Name of the list item node type that these shortcuts apply to |
import { ListKeymap } from '@domternal/core';
const editor = new Editor({ extensions: [ ListKeymap.configure({ listItem: 'listItem', }), ],});Commands
Section titled “Commands”ListKeymap does not register any commands. It uses ProseMirror’s sinkListItem and liftListItem commands from prosemirror-schema-list directly in the keyboard shortcut handlers.
Keyboard shortcuts
Section titled “Keyboard shortcuts”| Key | Action | Description |
|---|---|---|
Tab | Sink (indent) | Nests the current list item inside the previous sibling, creating a sub-list |
Shift-Tab | Lift (outdent) | Moves the current list item one level up in the list hierarchy |
Backspace | Lift at start | When the cursor is at the very start of a list item with an empty selection, lifts the item out of the list |
Tab (indent)
Section titled “Tab (indent)”Pressing Tab inside a list item indents it one level, wrapping it in a new sub-list under the previous sibling item. This uses ProseMirror’s sinkListItem command.
Before: After Tab on "Second":- First item - First item- Second item - Second item- Third item - Third itemIf there is no previous sibling (the item is first in the list), Tab does nothing.
Shift-Tab (outdent)
Section titled “Shift-Tab (outdent)”Pressing Shift-Tab moves the list item one level up. This uses ProseMirror’s liftListItem command.
Before: After Shift-Tab on "Nested":- First item - First item - Nested item - Nested itemIf the item is already at the top level, Shift-Tab lifts it out of the list entirely, converting it to a paragraph.
Backspace (lift at start)
Section titled “Backspace (lift at start)”Pressing Backspace when the cursor is at the very start of a list item (empty selection, parentOffset === 0) lifts the item out of its parent list. This only triggers when:
- The selection is empty (collapsed cursor)
- The cursor is at position 0 within the parent text block
- The cursor is at the start of the list item’s first child (within 1 position of the list item’s start)
Input rules
Section titled “Input rules”ListKeymap does not register any input rules.
Toolbar items
Section titled “Toolbar items”ListKeymap does not register any toolbar items.
How it works
Section titled “How it works”Cross-type protection
Section titled “Cross-type protection”ListKeymap includes a guard (getListItemContext) that prevents its shortcuts from interfering with other list-like structures such as task lists. Before executing any shortcut, the function walks up the document tree from the cursor position:
- If it finds the target
listItemtype first, the shortcut runs - If it encounters a different defining block node (like
taskItem) inside a list parent first, the shortcut returnsfalseand does nothing
This ensures that Tab/Shift-Tab inside a task list is handled by the TaskItem extension, not by ListKeymap.
ProseMirror commands
Section titled “ProseMirror commands”The sinkListItem and liftListItem commands come from @domternal/pm/schema-list (ProseMirror’s prosemirror-schema-list package). They handle the structural tree transformations needed to nest and unnest list items while preserving content and maintaining valid document structure.
Exports
Section titled “Exports”import { ListKeymap } from '@domternal/core';import type { ListKeymapOptions } from '@domternal/core';| Export | Type | Description |
|---|---|---|
ListKeymap | Extension | The list keymap extension |
ListKeymapOptions | TypeScript type | Options for ListKeymap.configure() |
Source
Section titled “Source”@domternal/core - ListKeymap.ts