Extend your DOCX export with Headers & Footers

Available in Team planBeta

With the @tiptap-pro/extension-export-docx-header-footer you can extend your DOCX export functionality by allowing you to customize the headers and footers of the exported document.

When adding this extension it will allow you to configure your ExportDocx with some additional properties:

Headers Configuration

The headers object allows you to customize the headers of your exported DOCX document:

PropertyTypeDescription
evenAndOddHeadersbooleanWhether to use different headers for odd and even pages
defaultHeader | (() => Promise<Header>)The standard default header on every page, or header on odd pages when the evenAndOddHeaders option is activated. Can be a Header object or an async function that returns a Header
firstHeader | (() => Promise<Header>)The header on the first page when the 'Different First Page' option is activated. Can be a Header object or an async function that returns a Header
evenHeader | (() => Promise<Header>)The header on even pages when the evenAndOddHeaders option is activated. Can be a Header object or an async function that returns a Header

Footers Configuration

The footers object allows you to customize the footers of your exported DOCX document:

PropertyTypeDescription
evenAndOddFootersbooleanWhether to use different footers for odd and even pages
defaultFooter | (() => Promise<Footer>)The standard default footer on every page, or footer on odd pages when the evenAndOddFooters option is activated. Can be a Footer object or an async function that returns a Footer
firstFooter | (() => Promise<Footer>)The footer on the first page when the 'Different First Page' option is activated. Can be a Footer object or an async function that returns a Footer
evenFooter | (() => Promise<Footer>)The footer on even pages when the evenAndOddFooters option is activated. Can be a Footer object or an async function that returns a Footer

Usage Examples

Extension Configuration

Headers and footers are configured through the ExportDocx.configure() method. You can use direct Docx namespace objects like Docx.Header and Docx.Footer for full control:

Note about Header and Footer Objects

The Header, Footer, Paragraph, and TextRun objects are accessed through the Docx namespace exported from @tiptap-pro/extension-export-docx. You can customize them with any valid content including paragraphs, text runs, and more if available within the standard elements that can be used within a DOCX header or footer.

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

const editor = new Editor({
  extensions: [
    // ... other extensions
    ExportDocx.configure({
      onCompleteExport: result => {
        // Handle the export 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 = 'export.docx'
        a.click()
        URL.revokeObjectURL(url)
      },
      headers: {
        evenAndOddHeaders: true,
        default: new Docx.Header({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  text: "Default Header",
                  bold: true,
                }),
              ],
            }),
          ],
        }),
        first: new Docx.Header({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  text: "First Page Header",
                  size: 24,
                  bold: true,
                }),
              ],
            }),
          ],
        }),
        even: new Docx.Header({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  text: "Even Page Header",
                }),
              ],
            }),
          ],
        }),
      },
      footers: {
        default: new Docx.Footer({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  text: "Default Footer",
                }),
              ],
            }),
          ],
        }),
      },
    }),
  ],
})

// Trigger export
editor.commands.exportDocx()

Using Helper Functions (Alternative Approach)

For easier handling of Tiptap-style content, you can use the convertHeader and convertFooter helper functions that automatically handle mark conversion, links, and other Tiptap features.

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

const editor = new Editor({
  extensions: [
    // ... other extensions
    ExportDocx.configure({
      onCompleteExport: result => {
        // Handle the export 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 = 'export.docx'
        a.click()
        URL.revokeObjectURL(url)
      },
      headers: {
        evenAndOddHeaders: true,
        default: async () =>
          convertHeader({
            node: {
              type: 'paragraph',
              content: [
                {
                  type: 'text',
                  text: 'Header',
                  marks: [{ type: 'textStyle', attrs: { color: 'red' } }],
                },
              ],
            },
          }),
        first: async () =>
          convertHeader({
            node: {
              type: 'paragraph',
              content: [
                {
                  type: 'text',
                  text: 'First Page Header',
                  marks: [{ type: 'bold' }],
                },
              ],
            },
          }),
        even: async () =>
          convertHeader({
            node: {
              type: 'paragraph',
              content: [
                {
                  type: 'text',
                  text: 'Even Page Header',
                  marks: [{ type: 'textStyle', attrs: { color: 'blue' } }],
                },
              ],
            },
          }),
      },
      footers: {
        default: async () =>
          convertFooter({
            node: {
              type: 'paragraph',
              content: [
                {
                  type: 'text',
                  text: 'Footer',
                  marks: [{ type: 'textStyle', attrs: { color: 'red' } }],
                },
              ],
            },
          }),
        first: async () =>
          convertFooter({
            node: {
              type: 'paragraph',
              content: [
                {
                  type: 'text',
                  text: 'First Page Footer',
                  marks: [{ type: 'bold' }],
                },
              ],
            },
          }),
        even: async () =>
          convertFooter({
            node: {
              type: 'paragraph',
              content: [
                {
                  type: 'text',
                  text: 'Even Page Footer',
                  marks: [{ type: 'textStyle', attrs: { color: 'blue' } }],
                },
              ],
            },
          }),
      },
    }),
  ],
})

// Trigger export
editor.commands.exportDocx()

The convertHeader and convertFooter helper functions expect a Tiptap node, which in this case it's a paragraph, and they will automatically handle:

  • Mark conversion (bold, italic, colors, etc.)
  • Link conversion
  • Text styling and formatting
  • Complex Tiptap node structures

This makes it much easier to create rich headers and footers using familiar Tiptap JSON structure.

Important note!

Be aware that if you use the convertHeader and convertFooter helpers you'd need to then use an asynchronous arrow functions as this convertHeader and convertFooter utility functions internally call convertParagraph helper which is an asynchronous function due to our image resolution implementation to handle images.

Advanced Examples

Dynamic Headers and Footers with Async Functions

You can also provide asynchronous functions that return Header or Footer objects. This is useful for dynamic content generation, such as fetching user data, formatting current dates, or processing images.

Async Function Benefits

Using async functions for headers and footers enables powerful use cases like: Fetching user information from APIs, including current timestamps or dynamic dates, processing and embedding images, calculating document statistics, retrieving data from databases or external services.

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

// Function to fetch user information
async function getCurrentUser() {
  // This could be an API call, database query, etc.
  return {
    name: 'John Doe',
    department: 'Engineering',
    email: 'john.doe@company.com'
  }
}

const editor = new Editor({
  extensions: [
    // ... other extensions
    ExportDocx.configure({
      onCompleteExport: result => {
        // Handle the export 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 = 'export.docx'
        a.click()
        URL.revokeObjectURL(url)
      },
      headers: {
        default: async () => {
          const user = await getCurrentUser()
          const currentDate = new Date().toLocaleDateString()
          
          return new Docx.Header({
            children: [
              new Docx.Paragraph({
                children: [
                  new Docx.TextRun({
                    text: `Document prepared by: ${user.name} (${user.department}) on ${currentDate}`,
                    size: 20,
                  }),
                ],
              }),
            ],
          })
        },
      },
      footers: {
        default: async () => {
          const user = await getCurrentUser()
          
          return new Docx.Footer({
            children: [
              new Docx.Paragraph({
                children: [
                  new Docx.TextRun({
                    text: `Contact: ${user.email} | Generated: ${new Date().toISOString()}`,
                    size: 16,
                    italics: true,
                  }),
                ],
              }),
            ],
          })
        },
      },
    }),
  ],
})

// Trigger export
editor.commands.exportDocx()

Async function lifecycle

The asynchronous functions provided will be called after the entire document conversion has been processed, right before constructing the document.

Mixed Static and Dynamic Headers/Footers

You can combine static headers/footers with dynamic ones for different pages:

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

const editor = new Editor({
  extensions: [
    // ... other extensions
    ExportDocx.configure({
      onCompleteExport: result => {
        // Handle the export 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 = 'export.docx'
        a.click()
        URL.revokeObjectURL(url)
      },
      headers: {
        // Static first page header
        first: new Docx.Header({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  text: "Company Confidential Report",
                  size: 24,
                  bold: true,
                }),
              ],
            }),
          ],
        }),
        // Dynamic default header for subsequent pages
        default: async () => {
          const reportData = await fetchReportMetadata()
          
          return new Docx.Header({
            children: [
              new Docx.Paragraph({
                children: [
                  new Docx.TextRun({
                    text: `${reportData.title} - Generated ${reportData.timestamp}`,
                    size: 18,
                  }),
                ],
              }),
            ],
          })
        },
      },
      footers: {
        default: async () => {
          const stats = await getDocumentStats()
          
          return new Docx.Footer({
            children: [
              new Docx.Paragraph({
                children: [
                  new Docx.TextRun({
                    text: `Page Count: ${stats.pages} | Word Count: ${stats.words}`,
                    size: 14,
                  }),
                ],
              }),
            ],
          })
        },
      },
    }),
  ],
})

async function fetchReportMetadata() {
  // Simulate API call
  return {
    title: 'Quarterly Business Report',
    timestamp: new Date().toLocaleDateString()
  }
}

async function getDocumentStats() {
  // Simulate document analysis
  return {
    pages: 10,
    words: 2500
  }
}

Page Numbering

The Page numbering functionality requires using the manual Docx namespace approach. The convertHeader and convertFooter helper functions do not currently support Docx.PageNumber features. You must use new Docx.Header() or new Docx.Footer() directly to access page numbering capabilities.

Basic Page Numbers

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

const editor = new Editor({
  extensions: [
    // ... other extensions
    ExportDocx.configure({
      onCompleteExport: result => {
        // Handle the export 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 = 'export.docx'
        a.click()
        URL.revokeObjectURL(url)
      },
      headers: {
        default: new Docx.Header({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  children: ['Page ', Docx.PageNumber.CURRENT],
                }),
              ],
            }),
          ],
        }),
      },
      footers: {
        default: new Docx.Footer({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  children: ['Page ', Docx.PageNumber.CURRENT],
                }),
              ],
            }),
          ],
        }),
      },
    }),
  ],
})

// Trigger export
editor.commands.exportDocx()

Page Numbers with Total Pages

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

const editor = new Editor({
  extensions: [
    // ... other extensions
    ExportDocx.configure({
      onCompleteExport: result => {
        // Handle the export 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 = 'export.docx'
        a.click()
        URL.revokeObjectURL(url)
      },
      footers: {
        default: new Docx.Footer({
          children: [
            new Docx.Paragraph({
              children: [
                new Docx.TextRun({
                  children: [
                    'Page ',
                    Docx.PageNumber.CURRENT,
                    ' of ',
                    Docx.PageNumber.TOTAL_PAGES,
                  ],
                }),
              ],
            }),
          ],
        }),
      },
    }),
  ],
})

// Trigger export
editor.commands.exportDocx()