From zero to print-ready: Pages & DOCX export

Alpha

This guide walks through setting up a Tiptap editor from scratch with the Pages extension for paginated, print-like layout, plus DOCX export and import for a professional, print-ready workflow.


1. Install the Pages stack

Install the three Pro packages that always go together when you build with Pages:

npm install @tiptap-pro/extension-convert-kit \
            @tiptap-pro/extension-pages-tablekit \
            @tiptap-pro/extension-pages

Why these three packages

@tiptap-pro/extension-convert-kit provides the editor's node and mark schema (paragraphs, headings, lists, images, marks). @tiptap-pro/extension-pages-tablekit provides pagination-safe tables — required, because ConvertKit's bundled tables are not designed to paginate. @tiptap-pro/extension-pages provides the page layout itself. Make sure you've authenticated to the Tiptap private npm registry first; see the Pro Extensions guide for setup.

2. Set up your editor with Pages

import { Editor } from '@tiptap/core'
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'
import { TableKit } from '@tiptap-pro/extension-pages-tablekit'
import { Pages } from '@tiptap-pro/extension-pages'

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

Best practice

Add Pages early in your setup so that document structure and layout are consistent from the start. You can change the page format, headers, and footers later via commands; it's harder to retrofit Pages onto a document that was already authored without pagination in mind.

3. Add DOCX export capability

Install the DOCX export extension:

npm install @tiptap-pro/extension-export-docx

Add it to your editor:

import { ExportDocx } from '@tiptap-pro/extension-export-docx'

const editor = new Editor({
  extensions: [
    ConvertKit.configure({ table: false }),
    TableKit,
    Pages.configure({
      /* ... */
    }),
    ExportDocx,
  ],
})

3a. Configure DOCX export

The DOCX export extension runs entirely in the browser — no JWT, no App ID, no server call. You configure it with an onCompleteExport callback that receives the result:

import { ExportDocx } from '@tiptap-pro/extension-export-docx'

ExportDocx.configure({
  // Required. The exported file is delivered through this callback.
  onCompleteExport: (result) => {
    const blob = new Blob([result], {
      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'document.docx'
    a.click()
    URL.revokeObjectURL(url)
  },
  // Optional: 'blob' (default), 'buffer', 'string', etc.
  exportType: 'blob',
  // Optional: override paragraph, heading, table styles, etc.
  styleOverrides: {},
  // Optional: define DOCX serialisation for any custom Tiptap nodes you've added.
  customNodes: [],
})

Required

onCompleteExport is required. The export does not return its result; it hands the result to your callback for you to download, upload, or process.

4. Export your document

Trigger an export from the editor's command chain:

editor.commands.exportDocx()

Round-trip is not byte-identical

DOCX export is faithful but not lossless. A few features (subscript/superscript marks, paragraph line-height, floating tables) round-trip imperfectly. See the feature support matrix for the full list.

5. Import DOCX files into your editor

Add the import extension so users can load Word documents into the paginated editor:

npm install @tiptap-pro/extension-import-docx
import { ImportDocx } from '@tiptap-pro/extension-import-docx'

const editor = new Editor({
  extensions: [
    ConvertKit.configure({ table: false }),
    TableKit,
    Pages.configure({
      /* ... */
    }),
    ExportDocx.configure({
      /* ... */
    }),
    ImportDocx.configure({
      appId: 'YOUR_APP_ID', // Your Convert App ID
      token: 'YOUR_JWT', // A JWT generated by your server
      // Optional: where the import service should upload images extracted from the document
      // imageUploadConfig: { url: 'https://your-server.com/upload-image' },
    }),
  ],
})

To trigger an import, pass a File from an <input type="file" />:

editor
  .chain()
  .importDocx({
    file,
    onImport(context) {
      if (context.error) {
        // Handle the failure (toast, log, retry, etc.)
        return
      }
      context.setEditorContent(context.content)
    },
  })
  .run()

When the imported document carries headers and footers, the Pages extension picks them up automatically — they appear at the top and bottom of each rendered page without further wiring.

Where to get your App ID and JWT

See the Conversion install guide for credential setup. JWTs must be generated server-side; never embed your secret key in the browser.


Next steps

  • Pages options — every option Pages exposes
  • Page header and footer — different first page, odd/even pages, programmatic editor open/close
  • PagesTableKit — pagination-safe tables in detail, including the most important known limitation
  • Limitations — non-splittable content, what Pages does not do, and how to work around each case

Need help?

Reach out to support if you hit something unexpected, or check the rest of the documentation for advanced workflows.