Bullet List
The BulletList node provides a block-level unordered list container (<ul>). It includes markdown-style input rules (-, *, or + followed by space), a keyboard shortcut (Mod-Shift-8), and automatic inclusion of the ListItem node and ListKeymap extension for Tab/Shift-Tab indentation.
When to use
Section titled “When to use”Choose BulletList when you need:
- Markdown
-,*, or+shortcuts to convert text into a bullet list - Nestable list items with Tab to indent and Shift+Tab to outdent
- Smart join when the user presses Backspace at the start of the first item
- Cross-list merging when typing a bullet between two existing lists
Skip it if:
- You need numbered items (use
OrderedListstandalone) - You need checkbox to-do behavior (use
TaskListinstead) - You need flat lists with no nesting (drop
ListKeymapandListIndent)
Live Playground
Section titled “Live Playground”Type -, *, or + followed by space at the start of a line to create a bullet list. Use Tab to indent and Shift-Tab to outdent.
The button above the editor is a custom HTML button wired to toggleBulletList(). Active state is tracked with editor.isActive('bulletList').
BulletList is included in StarterKit. If you are building a custom setup without StarterKit, add it manually:
import { Document, Text, Paragraph, BulletList } from '@domternal/core';import { DomternalEditor } from '@domternal/vanilla';
const dm = new DomternalEditor(document.getElementById('editor')!, { extensions: [Document, Text, Paragraph, BulletList], content: '<ul><li><p>First item</p></li><li><p>Second item</p></li></ul>',});import { Component, signal } from '@angular/core';import { DomternalEditorComponent } from '@domternal/angular';import { Editor, Document, Text, Paragraph, BulletList } from '@domternal/core';
@Component({ selector: 'app-editor', imports: [DomternalEditorComponent], template: ` <domternal-editor [extensions]="extensions" [content]="content" (editorCreated)="editor.set($event)" /> `,})export class EditorComponent { editor = signal<Editor | null>(null); extensions = [Document, Text, Paragraph, BulletList]; content = '<ul><li><p>First item</p></li><li><p>Second item</p></li></ul>';}import { Domternal } from '@domternal/react';import { Document, Text, Paragraph, BulletList } from '@domternal/core';
export default function Editor() { return ( <Domternal extensions={[Document, Text, Paragraph, BulletList]} content="<ul><li><p>First item</p></li><li><p>Second item</p></li></ul>" > <Domternal.Content /> </Domternal> );}<script setup lang="ts">import { Domternal } from '@domternal/vue';import { Document, Text, Paragraph, BulletList } from '@domternal/core';
const extensions = [Document, Text, Paragraph, BulletList];</script>
<template> <Domternal :extensions="extensions" content="<ul><li><p>First item</p></li><li><p>Second item</p></li></ul>"> <Domternal.Content /> </Domternal></template>Schema
Section titled “Schema”| Property | Value |
|---|---|
| ProseMirror name | bulletList |
| Type | Node |
| Group | block list |
| Content | listItem+ (one or more list items) |
| HTML tag | <ul> |
The list group allows ProseMirror to identify this node as a list container, which is used by list-related commands like toggleList.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
HTMLAttributes | Record<string, unknown> | {} | HTML attributes added to the <ul> element |
itemTypeName | string | 'listItem' | Name of the list item node type used by toggleList |
Custom HTML attributes
Section titled “Custom HTML attributes”import { BulletList } from '@domternal/core';
const CustomBulletList = BulletList.configure({ HTMLAttributes: { class: 'my-list' },});Commands
Section titled “Commands”| Command | Description |
|---|---|
toggleBulletList() | Toggle the whole list between bullet list and paragraph |
turnIntoBulletList() | Convert only the cursor’s item to a bullet list (Notion per-item turn-into) |
// Toggle bullet list on/offeditor.commands.toggleBulletList();
// With chainingeditor.chain().focus().toggleBulletList().run();toggleBulletList wraps the selected blocks in a <ul> with <li> items. If the selection is already inside a bullet list, it unwraps it back to paragraphs. If the selection is inside a different list type (ordered list, task list), it converts it to a bullet list.
Whole-list toggle vs per-item turn-into (v0.9.0)
Section titled “Whole-list toggle vs per-item turn-into (v0.9.0)”The two commands differ in scope when the cursor sits inside a list of another kind:
toggleBulletList()converts the whole list. Used by the toolbar button and theMod-Shift-8shortcut, preserving classic-editor semantics.turnIntoBulletList()converts only the cursor’s item and splits the run, leaving its siblings untouched (Notion-style). This is what the slash command’s “Bulleted list” entry and the block menu’s “Turn into” submenu use. Internally it callstoggleList(name, itemTypeName, undefined, { perItem: true }).
Keyboard shortcuts
Section titled “Keyboard shortcuts”| Shortcut | Command |
|---|---|
Mod-Shift-8 | toggleBulletList() |
Enter | Split list item at cursor, or lift out of list if empty |
Tab | Indent list item (sink) |
Shift-Tab | Outdent list item (lift) |
Backspace | Lift list item when at the start of an empty item |
The Enter, Tab, Shift-Tab, and Backspace shortcuts are provided by the ListItem node and ListKeymap extension, which are automatically included when you add BulletList.
Notion-style behaviors (v0.7.0)
Section titled “Notion-style behaviors (v0.7.0)”The list-item children-zone is now strictly paragraph block* (see List Item migration). Several Notion-style behaviors follow:
- Backspace on an empty paragraph immediately after a list: Deletes the paragraph and places the caret at the end of the last item (instead of wrapping it back as a list item).
- Backspace on an empty paragraph between two same-type lists: Joins the two lists back into one.
toggleListin a children-zone paragraph: Wraps that paragraph in a FRESH list (does not convert the ancestor list type).- Tab on a top-level paragraph immediately after a list: With the opt-in ListIndent extension (off by default), indents the paragraph INTO the previous list as a nested child. Without it, Tab moves focus to the next field as usual.
Input rules
Section titled “Input rules”| Input | Result |
|---|---|
- + space | Bullet list item |
* + space | Bullet list item |
+ + space | Bullet list item |
Type one of these characters at the start of a new line, then press space. The line wraps in a bullet list. These input rules only fire outside of existing lists - typing - + space inside a list item inserts the characters as plain text. To nest lists, use Tab to indent a list item.
The rule joins adjacent bullet lists on both sides: creating a bullet on a line that sits between two bullet lists merges all of them into a single list, rather than leaving the following list as a separate sibling.
Toolbar items
Section titled “Toolbar items”BulletList registers a button in the toolbar with the name bulletList in group lists at priority 200.
| Item | Command | Icon | Shortcut |
|---|---|---|---|
| Bullet List | toggleBulletList | listBullets | Mod-Shift-8 |
Included extensions
Section titled “Included extensions”BulletList automatically includes these extensions via addExtensions():
| Extension | Description |
|---|---|
| ListItem | The <li> node used inside the list |
| ListKeymap | Tab/Shift-Tab indentation and Backspace handling (included by ListItem) |
This means adding BulletList to your extensions array is sufficient. You don’t need to add ListItem or ListKeymap separately.
JSON representation
Section titled “JSON representation”{ "type": "bulletList", "content": [ { "type": "listItem", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "First item" } ] } ] }, { "type": "listItem", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Second item" } ] } ] } ]}A nested bullet list:
{ "type": "bulletList", "content": [ { "type": "listItem", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Parent item" } ] }, { "type": "bulletList", "content": [ { "type": "listItem", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Nested item" } ] } ] } ] } ] } ]}Source
Section titled “Source”@domternal/core - BulletList.ts
See also
Section titled “See also”- Ordered List - numbered list with
1.markdown shortcut - Task List - checkbox to-do list container
- List Item - list item used by bullet/ordered/task lists
- List Keymap - Backspace/Enter behavior for list items
- List Indent - Tab/Shift-Tab to sink/lift list items