How to Export Editor Content to Word (.docx) in React Tiptap Editor

The ExportWord extension in reactjs-tiptap-editor exposes an exportToWord command that serializes the ProseMirror document into a Word-compatible .docx file and triggers an automatic browser download.

The hunghg255/reactjs-tiptap-editor repository provides a dedicated solution for converting Tiptap editor content to Microsoft Word format. By leveraging the prosemirror-docx library alongside the docx npm package, this extension traverses the editor's node tree and generates properly formatted Word documents with support for tables, text styling, and line breaks.

How the ExportWord Extension Works

The export process follows a four-step pipeline implemented across several source files. Understanding this flow helps you debug issues or extend the functionality for custom node types.

Extension Registration

In src/extensions/ExportWord/ExportWord.ts, the extension registers the exportToWord command within the Tiptap editor ecosystem. When initialized, it accepts the current editor state (doc) and makes the export functionality available through the editor's command chain.

Document Serialization

The core conversion logic uses DocxSerializer from prosemirror-docx to walk the ProseMirror node tree. The extension defines custom node serializers in the same file to handle specific formatting:

  • Tables are configured to occupy 100% page width using WidthType.PERCENTAGE (lines 32-40)
  • Line breaks and paragraph spacing preserve the document structure
  • Images currently render as inline placeholders only, as binary embedding requires additional ArrayBuffer conversion via getImageBuffer

Blob Generation and Download

After serialization, the Document object from the docx library is converted to a binary Blob using Packer.toBlob. The helper function downloadFromBlob in src/utils/download.ts creates a temporary object URL and programmatically clicks an invisible anchor element, saving the file as richtext-export-document.docx by default.

UI Integration

The RichTextExportWord component in src/extensions/ExportWord/components/RichTextExportWord.tsx provides the toolbar button. It consumes the button configuration via useButtonProps and invokes the exportToWord command when clicked, as demonstrated in the playground at playground/src/App.tsx.

Implementation Guide

Integrating Word export capability requires three steps: importing the extension, adding it to your editor configuration, and rendering the trigger button.

1. Import the ExportWord Extension

Import the extension from the dedicated export path to enable the serialization logic.

import { ExportWord } from 'reactjs-tiptap-editor/exportword';

2. Configure Your Editor

Add ExportWord to your extensions array when initializing the Tiptap editor. The extension automatically registers the necessary commands and serializers.

import { useEditor } from '@tiptap/react';
import { Document } from '@tiptap/extension-document';
import { Paragraph } from '@tiptap/extension-paragraph';
import { Text } from '@tiptap/extension-text';
import { ExportWord } from 'reactjs-tiptap-editor/exportword';

const editor = useEditor({
  extensions: [
    Document,
    Paragraph,
    Text,
    ExportWord,  // Enables .docx export functionality
  ],
  content: '<p>Your initial content here</p>',
});

3. Add the Export Button to Your Toolbar

Place the RichTextExportWord component in your toolbar or action bar to provide the user interface for triggering the download.

import { RichTextExportWord } from 'reactjs-tiptap-editor/exportword';

function Toolbar() {
  return (
    <div className="flex gap-2">
      {/* Other toolbar items */}
      <RichTextExportWord />  {/* Click to download .docx */}
    </div>
  );
}

4. Complete Working Example

The following example from playground/src/App.tsx demonstrates a minimal implementation connecting all pieces together.

import { useEditor, EditorContent } from '@tiptap/react';
import { Document } from '@tiptap/extension-document';
import { Paragraph } from '@tiptap/extension-paragraph';
import { Text } from '@tiptap/extension-text';
import { ExportWord } from 'reactjs-tiptap-editor/exportword';
import { RichTextExportWord } from 'reactjs-tiptap-editor/exportword';

export default function App() {
  const editor = useEditor({
    extensions: [Document, Paragraph, Text, ExportWord],
    content: '<p>Export this content to Word format</p>',
  });

  return (
    <>
      <RichTextExportWord />
      <EditorContent editor={editor} />
    </>
  );
}

Technical Details and Limitations

When implementing Word export in production applications, consider these implementation specifics from the source code.

Image Handling Constraints

The current implementation in ExportWord.ts deliberately skips image embedding. The image serializer calls state.renderInline(node) to create a placeholder rather than embedding binary data. To support embedded images, extend the serializer to utilize the getImageBuffer helper, which fetches image URLs and returns Uint8Array data suitable for the docx library.

Table Formatting

Tables automatically expand to full page width (width: { size: 100, type: WidthType.PERCENTAGE }) to prevent content shrinking or collapsing in Word. This behavior is hardcoded in the table node serializer to ensure consistent Word document layouts regardless of browser rendering differences.

Source File Reference

These files contain the implementation details referenced throughout this guide:

Summary

  • Enable the extension by importing ExportWord from reactjs-tiptap-editor/exportword and adding it to your Tiptap extensions array.
  • Trigger downloads via the exportToWord command, which uses prosemirror-docx to serialize the ProseMirror document into a docx library Document object.
  • Use the UI component RichTextExportWord to provide a ready-made toolbar button that wires into the export command.
  • Handle limitations by noting that images export as placeholders only, and tables default to 100% width to maintain layout integrity.
  • Reference key files in src/extensions/ExportWord/ and src/utils/download.ts for customization or debugging.

Frequently Asked Questions

Can I export images to the Word document?

Currently, the ExportWord extension renders images as inline placeholders only. The source code in src/extensions/ExportWord/ExportWord.ts intentionally skips binary embedding to avoid complexity. However, the codebase includes a getImageBuffer utility that fetches image URLs as ArrayBuffer objects. You can extend the node serializer to utilize these buffers and embed Uint8Array data into the Word document if your use case requires image support.

How do I customize the filename of the exported document?

The default filename is richtext-export-document.docx, defined in the exportToWord command within ExportWord.ts. To customize this, you would need to fork or patch the extension to pass a custom filename parameter to the downloadFromBlob helper in src/utils/download.ts, which currently creates the download link with a hardcoded or default name.

Why do tables appear at 100% width in the exported file?

The serializer enforces full-width tables (WidthType.PERCENTAGE with size 100) in lines 32-40 of ExportWord.ts to prevent table content from shrinking or collapsing in Word. This ensures consistent rendering across different Word versions and screen sizes, regardless of the table's appearance in the browser editor.

Does this extension support all Tiptap node types?

The extension supports standard text nodes, paragraphs, headings, and tables through the custom nodeSerializer configuration. Complex or custom node types may require additional serializers in ExportWord.ts to map ProseMirror nodes to their docx library equivalents. Unsupported nodes will be skipped or rendered as plain text during serialization.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →