Numbering Format Dropdown Menu

Available in Start plan

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-menu

Setup

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.

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance.
formatsNumberingFormat[]DEFAULT_FORMATSThe numbering format ids to offer in the dropdown.
hideWhenUnavailablebooleanfalseHides the dropdown when an ordered list is not available.
modalbooleantrueWhether the dropdown traps focus as a modal.
onOpenChange(boolean) => voidundefinedCallback 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

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance.
formatNumberingFormatThe numbering format this button applies. Required.
hideWhenUnavailablebooleanfalseHides the button when an ordered list is not available.
onFormatSet() => voidundefinedCalled 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

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance.
formatsNumberingFormat[]DEFAULT_FORMATSThe numbering format ids to offer.
hideWhenUnavailablebooleanfalseHides the dropdown when an ordered list is unavailable.

Return Values

NameTypeDescription
isVisiblebooleanWhether the dropdown should be rendered.
currentFormatstringThe currently applied format id ("decimal" when none is set).
isActivebooleanWhether any of the offered formats is currently active.
canTogglebooleanWhether a numbering format can be set in the current state.
formatsNumberingFormat[]The offered format ids.
setFormat(format: NumberingFormat) => booleanApply a format; returns true on success.
isFormatActive(format: NumberingFormat) => booleanWhether a given format is the active one (for an active marker).
getFormatLabel(format: NumberingFormat) => stringHuman-readable preview label for a format, e.g. "1. 2. 3.".

useNumberingFormatButton()

State and handlers for a single format option.

Props

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance.
formatNumberingFormatThe format this button applies. Required.
hideWhenUnavailablebooleanfalseHides the button when unavailable.
onFormatSet() => voidundefinedCalled after the format is set.

Return Values

NameTypeDescription
isVisiblebooleanWhether the button should be rendered.
isActivebooleanWhether this format is currently active.
handleSetFormat() => booleanApplies the format; returns true on success.
canSetbooleanWhether the format can be set in the current state.
labelstringThe 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 the orderedListNumbering extension 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)