List Dropdown Menu
A fully accessible list dropdown menu for Tiptap editors. Easily toggle between different list types (bullet, ordered, task) with keyboard shortcut support and flexible customization options.
Installation
Add the component via the Tiptap CLI:
npx @tiptap/cli@latest add list-dropdown-menu
Components
<ListDropdownMenu />
A prebuilt React component that provides a dropdown menu for selecting different list types.
Usage
import { EditorContent, EditorContext, useEditor } from '@tiptap/react'
import { StarterKit } from '@tiptap/starter-kit'
import { TaskList } from '@tiptap/extension-task-list'
import { TaskItem } from '@tiptap/extension-task-item'
import { ListDropdownMenu } from '@/components/tiptap-ui/list-dropdown-menu'
import '@/components/tiptap-node/code-block-node/code-block-node.scss'
import '@/components/tiptap-node/list-node/list-node.scss'
import '@/components/tiptap-node/paragraph-node/paragraph-node.scss'
export default function MyEditor() {
const editor = useEditor({
immediatelyRender: false,
extensions: [StarterKit, TaskList, TaskItem.configure({ nested: true })],
content: `
<ul>
<li>
<strong>Bold</strong> for emphasis with <code>**</code> or <code>⌘+B</code> or the <code>B</code> button.
</li>
<li>
<em>Italic</em> for subtle nuances with <code>*</code> or <code>⌘+I</code> or the <code>I</code> button.
</li>
<li>
<s>Strikethrough</s> to show revisions with <code>~~</code> or the <code>~~S~~</code> button.
</li>
</ul>
<ul data-type="taskList">
<li data-type="taskItem" data-checked="true">
<div>
Test template
</div>
</li>
<li data-type="taskItem" data-checked="false">
<div>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev/pricing">Create account</a>
</div>
</li>
<li data-type="taskItem" data-checked="false">
<div>
Download free template
</div>
</li>
</ul>
`,
})
return (
<EditorContext.Provider value={{ editor }}>
<ListDropdownMenu
editor={editor}
types={['bulletList', 'orderedList', 'taskList']}
hideWhenUnavailable={true}
portal={false}
onOpenChange={(isOpen) => console.log('Dropdown opened:', isOpen)}
/>
<EditorContent editor={editor} role="presentation" />
</EditorContext.Provider>
)
}
Props
Name | Type | Default | Description |
---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
types | ListType[] | ["bulletList", "orderedList", "taskList"] | The list types to display in the dropdown |
hideWhenUnavailable | boolean | false | Hides the dropdown when no list types are available |
onOpenChange | (isOpen: boolean) => void | undefined | Callback fired when the dropdown opens or closes |
portal | boolean | false | Whether to render the dropdown menu in a portal |
Hooks
useListState()
A custom hook for managing list dropdown state and determining which options are available.
Usage
function MyListDropdown() {
const { listInSchema, filteredLists, canToggleAny, isAnyActive, isVisible, Icon } = useListState(
editor,
['bulletList', 'orderedList', 'taskList'],
true, // hideWhenUnavailable
)
if (!isVisible) return null
return (
<div>
<button disabled={!canToggleAny}>
<Icon />
Lists {isAnyActive ? '(Active)' : ''}
</button>
<div>
{filteredLists.map((option) => (
<button key={option.type}>
<option.icon />
{option.label}
</button>
))}
</div>
</div>
)
}
Props
Name | Type | Default | Description |
---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
availableTypes | ListType[] | undefined | Array of list types to check availability for |
hideWhenUnavailable | boolean | false | Whether to hide when no lists can be toggled |
Return Values
Name | Type | Description |
---|---|---|
listInSchema | boolean | Whether any list types are available in schema |
filteredLists | ListOption[] | List options filtered by available types |
canToggleAny | boolean | If any list type can be toggled |
isAnyActive | boolean | If any list type is currently active |
isVisible | boolean | Whether the dropdown should be rendered |
Icon | React.FC | Icon component for the active list or default |
Utilities
canToggleAnyList(editor, listTypes)
Checks if any of the specified list types can be toggled in the current editor state.
import { canToggleAnyList } from '@/components/tiptap-ui/list-dropdown-menu'
const canToggle = canToggleAnyList(editor, ['bulletList', 'orderedList'])
if (canToggle) {
console.log('At least one list type can be toggled')
}
isAnyListActive(editor, listTypes)
Checks if any of the specified list types are currently active in the editor.
import { isAnyListActive } from '@/components/tiptap-ui/list-dropdown-menu'
const isActive = isAnyListActive(editor, ['bulletList', 'orderedList', 'taskList'])
if (isActive) {
console.log('At least one list type is currently active')
}
getFilteredListOptions(availableTypes)
Filters the predefined list options based on the available types.
import { getFilteredListOptions } from '@/components/tiptap-ui/list-dropdown-menu'
const availableOptions = getFilteredListOptions(['bulletList', 'taskList'])
// Returns only bullet list and task list options
Keyboard Shortcuts
Each list type supports keyboard shortcuts (inherited from the underlying ListButton
components):
- Cmd/Ctrl + Shift + 8: Toggle bullet list
- Cmd/Ctrl + Shift + 7: Toggle ordered list
- Cmd/Ctrl + Shift + 9: Toggle task list
The shortcuts are automatically registered when the list types are available in the editor schema.
Requirements
Dependencies
@tiptap/react
- Core Tiptap React integration@tiptap/starter-kit
- Basic Tiptap extensions including list support@tiptap/extension-task-list
- Task list extension@tiptap/extension-task-item
- Task item extension
Referenced Components
use-tiptap-editor
(hook)list-button
(component)button
(primitive)dropdown-menu
(primitive)card
(primitive)tiptap-utils
(lib)chevron-down-icon
(icon)list-icon
(icon)list-ordered-icon
(icon)list-todo-icon
(icon)