Heading Button

Available for free

A fully accessible heading button for Tiptap editors. Toggle between different heading levels and paragraph text with keyboard shortcut support, level-specific icons, and tooltip integration.

Installation

Add the component via the Tiptap CLI:

npx @tiptap/cli@latest add heading-button

Components

<HeadingButton />

A prebuilt React component that toggles heading levels in the editor with full accessibility support.

Usage

import { HeadingButton } from '@/components/tiptap-ui/heading-button'
import { EditorContent, EditorContext, useEditor } from '@tiptap/react'
import { StarterKit } from '@tiptap/starter-kit'

import '@/components/tiptap-node/paragraph-node/paragraph-node.scss'

export default function MyEditor() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [StarterKit],
    content: `
          <h1>Heading 1</h1>
          <h2>Heading 2</h2>
        `,
  })

  return (
    <EditorContext.Provider value={{ editor }}>
      <HeadingButton
        editor={editor}
        level={1}
        text="Heading 1"
        hideWhenUnavailable={true}
        showShortcut={true}
        onToggled={() => console.log(`Heading ${level} toggled!`)}
      />
      <HeadingButton
        editor={editor}
        level={2}
        text="Heading 2"
        hideWhenUnavailable={true}
        showShortcut={true}
        onToggled={() => console.log(`Heading ${level} toggled!`)}
      />

      <EditorContent editor={editor} role="presentation" />
    </EditorContext.Provider>
  )
}

Props

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance
levelLevel (1-6)requiredThe heading level (1, 2, 3, 4, 5, or 6)
textstringundefinedOptional text label for the button
hideWhenUnavailablebooleanfalseHides the button when heading is not applicable
onToggled() => voidundefinedCallback fired after a successful heading toggle
showShortcutbooleanfalseShows a keyboard shortcut badge

Hooks

useHeading()

A custom hook to build your own heading button with full control over rendering and behavior.

Usage

function MyHeadingButton() {
  const { isVisible, isActive, canToggle, handleToggle, label, shortcutKeys, Icon } = useHeading({
    editor: myEditor,
    level: 3,
    hideWhenUnavailable: true,
    onToggled: () => console.log('Heading 3 toggled!'),
  })

  if (!isVisible) return null

  return (
    <button onClick={handleToggle} disabled={!canToggle} aria-label={label} aria-pressed={isActive}>
      <Icon />
      {label}
      {shortcutKeys && <HeadingShortcutBadge level={3} shortcutKeys={shortcutKeys} />}
    </button>
  )
}

Props

NameTypeDefaultDescription
editorEditor | nullundefinedThe Tiptap editor instance
levelLevel (1-6)requiredThe heading level
hideWhenUnavailablebooleanfalseHides the button if heading cannot be applied
onToggled() => voidundefinedCallback fired after toggling heading

Return Values

NameTypeDescription
isVisiblebooleanWhether the button should be rendered
isActivebooleanIf the heading level is currently active
canTogglebooleanIf the heading can be toggled
handleToggle() => booleanFunction to toggle heading formatting
labelstringAccessible label text for the button
shortcutKeysstringKeyboard shortcut for the specific level
IconReact.FCIcon component for the heading level

Utilities

canToggle(editor, level?, turnInto?)

Checks if heading can be toggled in the current editor state.

import { canToggle } from '@/components/tiptap-ui/heading-button'

const canToggleH2 = canToggle(editor, 2) // Check if can toggle to Heading 2
const canTurnInto = canToggle(editor, 1, true) // Check if can turn into Heading 1

isHeadingActive(editor, level?)

Checks if a heading level is currently active.

import { isHeadingActive } from '@/components/tiptap-ui/heading-button'

const isH1Active = isHeadingActive(editor, 1) // Check if Heading 1 is active
const isAnyHeadingActive = isHeadingActive(editor) // Check if any heading is active

toggleHeading(editor, level)

Programmatically toggles heading formatting for the current selection.

import { toggleHeading } from '@/components/tiptap-ui/heading-button'

const success = toggleHeading(editor, 3) // Toggle Heading 3
if (success) {
  console.log('Heading toggled successfully!')
}

Keyboard Shortcuts

The heading button supports level-specific keyboard shortcuts that are automatically registered:

  • Ctrl + Alt + 1: Toggle Heading 1
  • Ctrl + Alt + 2: Toggle Heading 2
  • Ctrl + Alt + 3: Toggle Heading 3
  • Ctrl + Alt + 4: Toggle Heading 4
  • Ctrl + Alt + 5: Toggle Heading 5
  • Ctrl + Alt + 6: Toggle Heading 6

The shortcuts are automatically registered when using either the <HeadingButton /> component or the useHeading() hook. Each heading level has its own unique shortcut.

Requirements

Dependencies

  • @tiptap/react - Core Tiptap React integration
  • @tiptap/starter-kit - Basic Tiptap extensions including heading support
  • react-hotkeys-hook - Keyboard shortcut management

Referenced Components

  • use-tiptap-editor (hook)
  • button (primitive)
  • badge (primitive)
  • tiptap-utils (lib)
  • heading-one-icon through heading-six-icon (icons)