Export custom nodes to .epub
The @tiptap-pro/extension-export-epub extension supports custom node conversion, allowing you to define how custom nodes in your Tiptap schema should be rendered in EPUB exports.
Requires Export DOCX extension
Custom node conversion for EPUB export requires the @tiptap-pro/extension-export-docx extension to be installed and configured in your editor. The EPUB export uses DOCX generation under the hood before converting to EPUB. Without it, the customNodes option will have no effect.
Export custom nodes to .epub
Pass your custom node definitions via the customNodes option when configuring ExportEpub:
import { ExportEpub } from '@tiptap-pro/extension-export-epub'
import { ExportDocx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
ExportDocx,
ExportEpub.configure({
token: 'YOUR_TOKEN',
appId: 'YOUR_APP_ID',
customNodes: [
{
type: 'hintbox',
render: node => {
// Define how your custom node renders in the document
},
},
],
onCompleteExport(result) {
// Handle the exported EPUB
},
}),
],
})Custom node conventions
Custom node converters must adhere to the underlying DOCX generation library's requirements. In practice, a custom converter function for DOCX should return one of the allowed DOCX elements for that node: a Paragraph class (or an array of Paragraph classes), a Table class, a TextRun class, an ExternalHyperlink class, or null if the node should be skipped in the output.
Define the custom node extension
For the sake of the example, suppose your editor has a custom node type hintbox (a callout-styled box). You can define how it should appear in the exported document.
Here's how the Hintbox extension's custom node might look like:
import { mergeAttributes, Node } from '@tiptap/core'
export interface ParagraphOptions {
/**
* The HTML attributes for a paragraph node.
* @default {}
* @example { class: 'foo' }
*/
HTMLAttributes: Record<string, any>
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
hintbox: {
/**
* Set a hintbox
* @example editor.commands.setHintbox()
*/
setHintbox: () => ReturnType
/**
* Toggle a hintbox
* @example editor.commands.toggleHintbox()
*/
toggleHintbox: () => ReturnType
}
}
}
/**
* This extension allows you to create hintboxes.
* @see https://www.tiptap.dev/api/nodes/paragraph
*/
export const Hintbox = Node.create<ParagraphOptions>({
name: 'hintbox',
priority: 1000,
addOptions() {
return {
HTMLAttributes: {
style: 'padding: 20px; border: 1px solid #b8d8ff; border-radius: 5px; background-color: #e6f3ff;',
},
}
},
group: 'block',
content: 'inline*',
parseHTML() {
return [{ tag: 'p' }]
},
renderHTML({ HTMLAttributes }) {
return ['p', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]
},
addCommands() {
return {
setHintbox:
() =>
({ commands }) => {
return commands.setNode(this.name)
},
toggleHintbox:
() =>
({ commands }) => {
return commands.toggleNode(this.name, 'paragraph')
},
}
},
addKeyboardShortcuts() {
return {
'Mod-Alt-h': () => this.editor.commands.toggleHintbox(),
}
},
})Define the custom node render function
And we will define how the Hintbox custom node should be rendered in the exported document:
import { ExportEpub } from '@tiptap-pro/extension-export-epub'
import {
convertTextNode,
Docx,
ExportDocx,
lineHeightToDocx,
pixelsToHalfPoints,
pointsToTwips,
} from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
// Other extensions ...
ExportDocx,
ExportEpub.configure({
token: 'YOUR_TOKEN',
appId: 'YOUR_APP_ID',
onCompleteExport: result => {
const url = URL.createObjectURL(result)
const a = document.createElement('a')
a.href = url
a.download = 'export.epub'
a.click()
URL.revokeObjectURL(url)
},
customNodes: [
{
type: 'hintbox',
render: node => {
return new Docx.Paragraph({
children: node.content.map(content => convertTextNode(content)),
style: 'Hintbox',
})
},
},
],
styleOverrides: {
paragraphStyles: [
{
id: 'Hintbox',
name: 'Hintbox',
basedOn: 'Normal',
next: 'Normal',
quickFormat: false,
run: {
font: 'Aptos Light',
size: pixelsToHalfPoints(16),
},
paragraph: {
spacing: {
before: pointsToTwips(12),
after: pointsToTwips(12),
line: lineHeightToDocx(1),
},
border: {
top: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
bottom: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
right: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
left: { style: Docx.BorderStyle.SINGLE, size: 1, color: 'b8d8ff', space: 5 },
},
shading: {
type: Docx.ShadingType.SOLID,
color: 'e6f3ff',
},
},
},
],
},
}),
// Other extensions ...
],
})You can construct any supported DOCX elements in the render function using the Docx library classes (Paragraph, TextRun, Table, etc.) that are provided via the Docx import from the @tiptap-pro/extension-export-docx package.