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-menuComponents
<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
useListDropdownMenu()
A custom hook for managing list dropdown state and determining which options are available.
Usage
import { useListDropdownMenu } from '@/components/tiptap-ui/list-dropdown-menu'
import { ListButton } from '@/components/tiptap-ui/list-button'
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from '@/components/tiptap-ui-primitive/dropdown-menu'
import { ButtonGroup } from '@/components/tiptap-ui-primitive/button'
function MyListDropdown() {
const { filteredLists, canToggle, isActive, isVisible, Icon, activeType } = useListDropdownMenu({
editor,
types: ['bulletList', 'orderedList', 'taskList'],
hideWhenUnavailable: true,
})
if (!isVisible) return null
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button disabled={!canToggle}>
<Icon />
Lists {isActive ? '(Active)' : ''}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<ButtonGroup>
{filteredLists.map((option) => (
<DropdownMenuItem key={option.type} asChild>
<ListButton editor={editor} type={option.type} text={option.label} />
</DropdownMenuItem>
))}
</ButtonGroup>
</DropdownMenuContent>
</DropdownMenu>
)
}Props
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
types | ListType[] | ["bulletList", "orderedList", "taskList"] | Array of list types to display in the dropdown |
hideWhenUnavailable | boolean | false | Whether to hide when no lists can be toggled |
Return Values
| Name | Type | Description |
|---|---|---|
isVisible | boolean | Whether the dropdown should be rendered |
activeType | ListType | undefined | Currently active list type |
isActive | boolean | If any list type is currently active |
canToggle | boolean | If any list type can be toggled |
types | ListType[] | Array of list types from configuration |
filteredLists | ListOption[] | List options filtered by available types |
label | string | Accessible label text for the dropdown |
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')
}Parameters
| Name | Type | Description |
|---|---|---|
editor | Editor | null | The Tiptap editor instance |
listTypes | ListType[] | Array of list types to check |
Returns
boolean - Whether 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')
}Parameters
| Name | Type | Description |
|---|---|---|
editor | Editor | null | The Tiptap editor instance |
listTypes | ListType[] | Array of list types to check |
Returns
boolean - Whether 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 optionsParameters
| Name | Type | Description |
|---|---|---|
availableTypes | ListType[] | Array of list types to include |
Returns
ListOption[] - Filtered array of list options.
getActiveListType(editor, availableTypes)
Gets the currently active list type from the available types.
import { getActiveListType } from '@/components/tiptap-ui/list-dropdown-menu'
const activeType = getActiveListType(editor, ['bulletList', 'orderedList', 'taskList'])
console.log('Current list type:', activeType)Parameters
| Name | Type | Description |
|---|---|---|
editor | Editor | null | The Tiptap editor instance |
availableTypes | ListType[] | Array of list types to check |
Returns
ListType | undefined - The active list type, or undefined if no list is active.
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)