Numbering Format Dropdown Menu
A numbering-style picker for ordered lists. It renders a ListButton to toggle the ordered list alongside a dropdown of numbering styles — decimal, parenthesised, nested decimal, upper/lower alpha, upper/lower roman, and zero-padded — each shown as a small nested-list preview.
The active format is read from and applied through the orderedListNumbering extension (part of @tiptap-pro/extension-convert-kit), so the on-screen markers always match the format that was registered. All editor logic is exposed by the useNumberingFormatDropdownMenu hook, so you can build a fully custom UI on top of it.
Requires the orderedListNumbering extension
This component reads and writes a numberingFormat id on the outermost ordered list. The matching
level definitions (what each id renders as on screen and on export) are supplied by the extension
config — see Setup below. Without it, selecting a format has no visible effect.
Installation
Add the component via the Tiptap CLI:
npx @tiptap/cli@latest add numbering-format-dropdown-menuSetup
The component only stores a format id on the list; the extension owns the actual marker rendering. Configure orderedListNumbering (via ConvertKit) with one level definition per format id you want to offer, and the same definitions can be passed to ExportDocx for a faithful round-trip.
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'
const NUMBERING_FORMATS = [
{
id: 'decimal',
levels: Array.from({ length: 9 }, (_v, depth) => ({
baseStyle: ['decimal', 'lowerLetter', 'lowerRoman'][depth % 3],
textTemplate: `%${depth + 1}.`,
})),
},
// …one entry per id you offer (see NUMBERING_FORMAT_IDS)
]
const editor = useEditor({
extensions: [
ConvertKit.configure({
orderedListNumbering: {
formats: NUMBERING_FORMATS,
defaultFormat: 'decimal',
},
}),
// …
],
})Components
<NumberingFormatDropdownMenu />
A prebuilt component that pairs an ordered-list toggle with the numbering-style dropdown.
Usage
export default function MyEditor() {
return (
<NumberingFormatDropdownMenu
editor={editor}
hideWhenUnavailable={true}
modal={false}
onOpenChange={(isOpen) => console.log('Dropdown', isOpen ? 'opened' : 'closed')}
/>
)
}Props
The component also forwards any extra Button props (except type) to the dropdown trigger.
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance. |
formats | NumberingFormat[] | DEFAULT_FORMATS | The numbering format ids to offer in the dropdown. |
hideWhenUnavailable | boolean | false | Hides the dropdown when an ordered list is not available. |
modal | boolean | true | Whether the dropdown traps focus as a modal. |
onOpenChange | (boolean) => void | undefined | Callback for when the dropdown opens or closes. |
<NumberingFormatButton />
A single format option, rendered as a small nested-list preview. Used to build the dropdown grid, or on its own.
Props
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance. |
format | NumberingFormat | — | The numbering format this button applies. Required. |
hideWhenUnavailable | boolean | false | Hides the button when an ordered list is not available. |
onFormatSet | () => void | undefined | Called after the format is successfully applied. |
Hooks
useNumberingFormatDropdownMenu()
A headless hook with the full state and actions needed to build a custom numbering dropdown.
Usage
function MyNumberingDropdown() {
const {
isVisible,
currentFormat,
canToggle,
formats,
setFormat,
isFormatActive,
getFormatLabel,
} = useNumberingFormatDropdownMenu({ editor, hideWhenUnavailable: true })
if (!isVisible) return null
return (
<DropdownMenu>
<DropdownMenuTrigger disabled={!canToggle}>Numbering</DropdownMenuTrigger>
<DropdownMenuContent>
{formats.map((format) => (
<DropdownMenuItem
key={format}
data-active-state={isFormatActive(format) ? 'on' : 'off'}
onClick={() => setFormat(format)}
>
{getFormatLabel(format)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}Props
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance. |
formats | NumberingFormat[] | DEFAULT_FORMATS | The numbering format ids to offer. |
hideWhenUnavailable | boolean | false | Hides the dropdown when an ordered list is unavailable. |
Return Values
| Name | Type | Description |
|---|---|---|
isVisible | boolean | Whether the dropdown should be rendered. |
currentFormat | string | The currently applied format id ("decimal" when none is set). |
isActive | boolean | Whether any of the offered formats is currently active. |
canToggle | boolean | Whether a numbering format can be set in the current state. |
formats | NumberingFormat[] | The offered format ids. |
setFormat | (format: NumberingFormat) => boolean | Apply a format; returns true on success. |
isFormatActive | (format: NumberingFormat) => boolean | Whether a given format is the active one (for an active marker). |
getFormatLabel | (format: NumberingFormat) => string | Human-readable preview label for a format, e.g. "1. 2. 3.". |
useNumberingFormatButton()
State and handlers for a single format option.
Props
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance. |
format | NumberingFormat | — | The format this button applies. Required. |
hideWhenUnavailable | boolean | false | Hides the button when unavailable. |
onFormatSet | () => void | undefined | Called after the format is set. |
Return Values
| Name | Type | Description |
|---|---|---|
isVisible | boolean | Whether the button should be rendered. |
isActive | boolean | Whether this format is currently active. |
handleSetFormat | () => boolean | Applies the format; returns true on success. |
canSet | boolean | Whether the format can be set in the current state. |
label | string | The preview label for the format, e.g. "1. 2. 3.". |
Utilities
Format ids and labels
import {
NUMBERING_FORMAT_IDS,
NUMBERING_FORMAT_LABELS,
DEFAULT_FORMATS,
} from '@/components/tiptap-ui/numbering-format-dropdown-menu'NumberingFormat is one of: decimal, decimal-paren, decimal-nested, upper-alpha, lower-alpha, upper-roman, lower-roman, decimal-zero. NUMBERING_FORMAT_IDS lists them in dropdown order; DEFAULT_FORMATS is the default offered set; NUMBERING_FORMAT_LABELS maps each id to its preview string (e.g. "A. B. C.").
Editor helpers
import {
setNumberingFormat,
isNumberingFormatActive,
getCurrentNumberingFormat,
canSetNumberingFormat,
getFormatLabel,
} from '@/components/tiptap-ui/numbering-format-dropdown-menu'
setNumberingFormat(editor, 'upper-roman') // applies the format, returns boolean
getCurrentNumberingFormat(editor) // active format id, or null when not in a list
isNumberingFormatActive(editor, 'decimal') // boolean
canSetNumberingFormat(editor) // boolean
getFormatLabel('lower-alpha') // "a. b. c."Requirements
Dependencies
@tiptap/react— Core Tiptap React integration@tiptap-pro/extension-convert-kit— Provides theorderedListNumberingextension that stores and renders the format
Referenced Components
use-tiptap-editor(hook)list-button(component)button(primitive)button-group(primitive)dropdown-menu(primitive)tiptap-utils(lib)chevron-down-icon(icon)