Skip to content

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.

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.

Click to try it out

BulletList is included in StarterKit. If you are building a custom setup without StarterKit, add it manually:

import { Editor, Document, Text, Paragraph, BulletList } from '@domternal/core';
const editor = new Editor({
element: document.getElementById('editor')!,
extensions: [Document, Text, Paragraph, BulletList],
content: '<ul><li><p>First item</p></li><li><p>Second item</p></li></ul>',
});
PropertyValue
ProseMirror namebulletList
TypeNode
Groupblock list
ContentlistItem+ (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.

OptionTypeDefaultDescription
HTMLAttributesRecord<string, unknown>{}HTML attributes added to the <ul> element
itemTypeNamestring'listItem'Name of the list item node type used by toggleList
import { BulletList } from '@domternal/core';
const CustomBulletList = BulletList.configure({
HTMLAttributes: { class: 'my-list' },
});
CommandDescription
toggleBulletList()Toggle the current selection between bullet list and paragraph
// Toggle bullet list on/off
editor.commands.toggleBulletList();
// With chaining
editor.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.

ShortcutCommand
Mod-Shift-8toggleBulletList()
EnterSplit list item at cursor, or lift out of list if empty
TabIndent list item (sink)
Shift-TabOutdent list item (lift)
BackspaceLift 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.

InputResult
- + spaceBullet list item
* + spaceBullet list item
+ + spaceBullet 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.

BulletList registers a button in the toolbar with the name bulletList in group lists at priority 200.

ItemCommandIconShortcut
Bullet ListtoggleBulletListlistBulletsMod-Shift-8

BulletList automatically includes these extensions via addExtensions():

ExtensionDescription
ListItemThe <li> node used inside the list
ListKeymapTab/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.

{
"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" }
]
}
]
}
]
}
]
}
]
}

@domternal/core - BulletList.ts