insertUnorderedList

How insertUnorderedList inputType creates unordered lists and varies across browsers.

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)

HTML:
<p>Item 1</p>
<p>Item 2</p>
<p>Item 3</p>

After insertUnorderedList

HTML:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<p>Item 3</p>
Selected paragraphs converted to list items

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:

Slate.js

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 wrapNodes with unordered-list type.
  • Type distinction: Only the list container type differs from ordered lists.
  • Consistent API: Same Transforms methods work for both list types.
ProseMirror

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_list instead of ordered_list.
  • Same command: Uses the same wrapIn command with different node type.
Draft.js

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.toggleBlockType like ordered lists.

Related resources