Pages extension API reference

interface PagesOptions {
  /**
   * Page format that determines page size and margins.
   * Can be a built-in format name or a custom page format object.
   * Built-in formats: A4, A3, A5, Letter, Legal, Tabloid
   * @default 'A4'
   */
  pageFormat: 'A4' | 'A3' | 'A5' | 'Letter' | 'Legal' | 'Tabloid' | PageFormat

  /**
   * Height of the page header in pixels.
   * @default 50
   */
  headerHeight?: number

  /**
   * Height of the page footer in pixels.
   * @default 50
   */
  footerHeight?: number

  /**
   * Gap between pages in pixels.
   * @default 50
   */
  pageGap?: number

  /**
   * Header content for every page. Can be a string (with tokens) or a function.
   * @default ''
   */
  header?: string | ((page: number, total: number) => string)

  /**
   * Footer content for every page. Can be a string (with tokens) or a function.
   * @default '
   */
  footer?: string | ((page: number, total: number) => string)

  /**
   * Background color for the page gap area.
   * @default undefined
   */
  pageGapBackground?: string

  /**
   * Callback function called when the page format changes.
   * Receives the new page format as an argument.
   * @default () => {}
   */
  onPageFormatChange?: (pageFormat: PageFormat) => void

  /**
   * Initial zoom level for visual scaling. Clamped to [0.25, 4].
   * @default 1
   */
  zoom?: number

  /**
   * Callback function called when the zoom level changes.
   * Receives the new zoom level as an argument.
   */
  onZoomChange?: (zoom: number) => void
}

interface PageFormat {
  name: string
  width: number    // Width in pixels
  height: number   // Height in pixels
  margins: {
    top: number    // Margin in pixels
    right: number  // Margin in pixels
    bottom: number // Margin in pixels
    left: number   // Margin in pixels
  }
}
OptionType / ValuesDefaultDescription
pageFormatstring | PageFormat'A4'Built-in format name or custom page format object
headerHeightnumber (px)50Header height in pixels
footerHeightnumber (px)50Footer height in pixels
pageGapnumber (px)50Gap between pages in pixels
headerstring | fn''Header content; string (supports {page}/{total}) or (pageNumber, totalPages) => string
footerstring | fn'{page}'Footer content; string (supports {page}/{total}) or (pageNumber, totalPages) => string
pageGapBackgroundstring (CSS color)undefinedBackground color for page gaps
onPageFormatChangefn() => {}Callback when page format changes; receives new PageFormat
zoomnumber1Initial zoom level (0.25–4). See Zoom
onZoomChangefnundefinedCallback when zoom changes; receives new zoom level

Example usage

Basic configuration with built-in format

import { Pages } from '@tiptap-pro/extension-pages'

const editor = new Editor({
  extensions: [
    Pages.configure({
      pageFormat: 'A4',
      headerHeight: 60,
      footerHeight: 40,
      pageGap: 40,
      header: 'My Project',
      footer: 'Page {page} of {total}',
      pageGapBackground: '#f8f8f8',
    }),
  ],
})

Custom page format configuration

import { Pages } from '@tiptap-pro/extension-pages'

const editor = new Editor({
  extensions: [
    Pages.configure({
      pageFormat: {
        name: 'Custom Format',
        width: 600,
        height: 800,
        margins: {
          top: 40,
          right: 30,
          bottom: 40,
          left: 30,
        },
      },
      header: 'Custom Document',
      footer: '{page} / {total}',
      onPageFormatChange: (pageFormat) => {
        console.log('Page format changed:', pageFormat.name)
        // Save user preference, update UI, etc.
      },
    }),
  ],
})

Available commands

The Pages extension provides the following commands:

CommandParametersDescription
setPageFormatpageFormat: PageFormatChanges the page format programmatically
setZoomzoom: numberSets the zoom level (clamped to 0.25–4). See Zoom

Command usage

// Change to built-in format
editor.commands.setPageFormat('Letter')

// Change to custom format
editor.commands.setPageFormat({
  id: 'wide-page-format',
  width: 1000, // px
  height: 700, // px
  margins: { top: 20, right: 20, bottom: 20, left: 20 } // px
})

Note

The header and footer options accept either a string (with tokens {page} and {total}) or a function (page, total) => string for dynamic content.

When a header or footer editor overlay is open, the Pages extension exposes the active inner editor through editor.storage.pages. This is useful for custom toolbars, status UI, and listening to editor events inside the header or footer overlay.

Storage propertyTypeDescription
activeEditorEditor | nullActive header/footer inner editor, or null when no overlay is active
activeEditorType'header' | 'footer' | nullWhether the active inner editor belongs to a header or footer
activePageNumbernumber | nullPage number for the active header/footer editor
headerEditorOnEditor['on'] | nullBound event subscription helper for the header inner editor
headerEditorOffEditor['off'] | nullBound event unsubscribe helper for the header inner editor
footerEditorOnEditor['on'] | nullBound event subscription helper for the footer inner editor
footerEditorOffEditor['off'] | nullBound event unsubscribe helper for the footer inner editor

The headerEditorOn / headerEditorOff and footerEditorOn / footerEditorOff helpers let you subscribe to events from the header and footer editors. These are useful for reacting to events like focus, selection updates, or content changes.

useEffect(() => {
  if (!editor) return

  const handleFocus = () => {
    const { activeEditorType, activePageNumber } = editor.storage.pages
    console.log('Editing', activeEditorType, activePageNumber)
  }

  editor.storage.pages.headerEditorOn?.('focus', handleFocus)
  editor.storage.pages.footerEditorOn?.('focus', handleFocus)

  return () => {
    editor.storage.pages.headerEditorOff?.('focus', handleFocus)
    editor.storage.pages.footerEditorOff?.('focus', handleFocus)
  }
}, [editor])

See Page header and footer for a complete toolbar example.