Overview
The insertUnorderedList inputType is triggered when the user creates an unordered list (typically via toolbar or programmatically). The browser wraps selected paragraphs in a <ul> with <li> items.
Basic Behavior
Scenario: Multiple paragraphs selected
Before (Paragraphs selected)
After insertUnorderedList
IME Composition + insertUnorderedList
⚠️ Critical Issue
During IME composition, creating an unordered list may cancel the composition or commit it partially. The composition text may be lost or inserted incorrectly into list items.
See: insertOrderedList for similar issues.
Editor-Specific Handling
Different editor frameworks handle unordered list creation similarly to ordered lists. Here's how major editors implement insertUnorderedList:
Unordered List Creation
Slate uses Transforms.wrapNodes() with unordered-list type:
import { Editor, Transforms, Element } from 'slate';
element.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertUnorderedList') {
e.preventDefault();
// Check if already in a list
const [match] = Editor.nodes(editor, {
match: n => Element.isElement(n) && n.type === 'unordered-list',
});
if (match) {
// Unwrap list
Transforms.unwrapNodes(editor, {
match: n => Element.isElement(n) && n.type === 'unordered-list',
});
} else {
// Wrap in unordered list
Transforms.wrapNodes(editor, {
type: 'unordered-list',
children: [],
});
// Convert blocks to list items
Transforms.setNodes(editor, {
type: 'list-item',
});
}
}
});
- Same pattern as ordered lists: Uses
wrapNodeswithunordered-listtype. - Type distinction: Only the list container type differs from ordered lists.
- Consistent API: Same Transforms methods work for both list types.
Unordered List Creation
ProseMirror uses wrapIn with bullet_list node type:
import { wrapIn } from 'prosemirror-commands';
import { schema } from './schema';
view.dom.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertUnorderedList') {
e.preventDefault();
const { state, dispatch } = view;
const listType = schema.nodes.bullet_list;
if (wrapIn(listType)(state, dispatch)) {
// Handled
}
}
});
- bullet_list node: Uses
schema.nodes.bullet_listinstead ofordered_list. - Same command: Uses the same
wrapIncommand with different node type.
Unordered List Creation
Draft.js uses RichUtils.toggleBlockType() with unordered-list-item:
import { EditorState, RichUtils } from 'draft-js';
function handleInsertUnorderedList(editorState) {
return RichUtils.toggleBlockType(editorState, 'unordered-list-item');
}
element.addEventListener('beforeinput', (e) => {
if (e.inputType === 'insertUnorderedList') {
e.preventDefault();
const newState = handleInsertUnorderedList(editorState);
setEditorState(newState);
}
});
- Block type: Uses
'unordered-list-item'block type. - Same utility: Uses
RichUtils.toggleBlockTypelike ordered lists.