Extend your DOCX export with Headers & Footers
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:
Property | Type | Description |
---|---|---|
evenAndOddHeaders | boolean | Whether to use different headers for odd and even pages |
default | Header | (() => 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 |
first | Header | (() => 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 |
even | Header | (() => 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:
Property | Type | Description |
---|---|---|
evenAndOddFooters | boolean | Whether to use different footers for odd and even pages |
default | Footer | (() => 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 |
first | Footer | (() => 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 |
even | Footer | (() => 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()