Export Markdown and PDF
Connect conversion packages to the File menu in React and Vue.
Connect conversion handlers to File > Export for Markdown and PDF downloads. Each export converts the bytes that save() returns.
Install converters
Editor packages do not include converters. Install the converter for each format that you enable:
| Format | Package | Runs in | License |
|---|---|---|---|
| Markdown | @docx-editor.dev/docx-to-markdown | Browser or Node.js | Apache-2.0 |
@docx-editor.dev/docx-to-pdf | Node.js server only | EigenPal Pro License |
# Browser application
npm install @docx-editor.dev/docx-to-markdown @docx-editor.dev/core
# Node.js server
npm install @docx-editor.dev/docx-to-pdf @docx-editor.dev/coreKeep PDF imports in server code, out of browser bundles. Opening the editor or menu does not load a converter.
Connect export handlers
Create exporters.ts with browser Markdown conversion and a PDF endpoint:
import type { ChromeExportHandlers } from '@docx-editor.dev/core/editor';
export const exporters: ChromeExportHandlers = {
async markdown(source) {
const { exportMarkdown } = await import('@docx-editor.dev/docx-to-markdown');
return exportMarkdown(source, { displayMode: 'proposed' });
},
pdf: async (source, view) => {
const response = await fetch('/api/export/pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'X-Revision-Markup': encodeURIComponent(JSON.stringify(view ?? {})),
},
body: source.slice().buffer,
});
const contentType = response.headers.get('Content-Type') ?? '';
if (!response.ok || !contentType.includes('application/pdf')) {
throw new Error(
'PDF export is unavailable. Configure a Node.js server with ' +
'@docx-editor.dev/docx-to-pdf.'
);
}
return { bytes: new Uint8Array(await response.arrayBuffer()) };
},
};Pass the handlers through menu.exporters:
import { DocxEditor } from '@docx-editor.dev/react';
import { exporters } from './exporters';
import '@docx-editor.dev/core/styles/editor.css';
export function Editor({ source }: { source: Uint8Array }) {
return <DocxEditor document={source} menu={{ exporters }} />;
}<script setup lang="ts">
import { DocxEditor } from '@docx-editor.dev/vue';
import { exporters } from './exporters';
import '@docx-editor.dev/core/styles/editor.css';
defineProps<{ source: Uint8Array }>();
</script>
<template>
<DocxEditor :document="source" :menu="{ exporters }" />
</template>For a composed editor, pass exporters to DocxEditor.Menu or DocxEditorMenu. The menu downloads result.markdown as continuous text; it does not join result.pages. File > Print uses the same PDF handler; see Print documents.
Convert PDF on the server
Your endpoint calls exportPdf and returns result.bytes as application/pdf. This Fetch API handler runs on Node.js:
import { exportPdf } from '@docx-editor.dev/docx-to-pdf';
import type { ChromeExportView } from '@docx-editor.dev/core/editor';
export async function POST(request: Request): Promise<Response> {
const source = new Uint8Array(await request.arrayBuffer());
const view = JSON.parse(
decodeURIComponent(request.headers.get('X-Revision-Markup') ?? '{}')
) as Partial<ChromeExportView>;
const result = await exportPdf(source, {
displayMode: view.displayMode ?? 'proposed',
revisionMarkup: view.revisionMarkup,
revisionAuthorSlots: view.revisionAuthorSlots,
comments: view.comments,
fidelityPolicy: 'strict',
useSystemFonts: false,
});
return new Response(result.bytes.slice().buffer, {
headers: {
'Content-Type': 'application/pdf',
'Cache-Control': 'no-store',
},
});
}The handler's second argument is a ChromeExportView. Forward all four fields to exportPdf:
| Field | Holds |
|---|---|
displayMode | The review view, or the markup chosen in print settings. |
revisionMarkup | The resolved markup preferences captured before conversion. |
revisionAuthorSlots | Stable author color slots from the editor session. |
comments | Whether comments print. When absent, the converter shows comments. |
A one-argument handler still works but uses its own export defaults. The converter validates the display mode and markup preferences.
Your server must also:
- Return an error for invalid JSON or invalid preferences.
- Add authentication, upload limits, and error responses for your application.
For complete servers, see Integrate PDF conversion.
Build custom export controls
The shared slots are file.exportMarkdown and file.exportPdf, exposed as DocxEditor.Menu.ExportMarkdown and DocxEditor.Menu.ExportPdf. This example hides PDF export inside DocxEditor.Root:
<DocxEditor.Menu exporters={exporters}>
<DocxEditor.Menu.File>
<DocxEditor.Menu.ExportPdf hidden />
</DocxEditor.Menu.File>
</DocxEditor.Menu>In Vue, use DocxEditorMenu with the same parts. If you replace the File menu with preset={false} (Vue: :preset="false"), put these parts inside a DocxEditor.Menu.Submenu with labelKey="toolbar.export". Generic Menu.Item dispatches editing commands and does not call converters.
For your own download UI, call runChromeExport(editor, format, exporters) from @docx-editor.dev/core/editor. It returns the bytes, a filename extension, and a media type.
Limits
- Export calls
save(), which can validate form input and refresh field results with an undo step. See Save a document. - A missing handler never falls back to another format. The menu shows the required package and configuration.
runChromeExport()rejects a missing handler withChromeExportError, whosecodeismissing-exporter.- The editor checks the PDF header before download. A failed conversion or another file type shows an error.
- Continue editing dismisses the progress dialog without stopping the export. Export actions stay disabled until conversion finishes.
- To replace progress and error feedback, use
popups.export. See Customize export feedback.