JSPM

  • Created
  • Published
  • Downloads 1894
  • Score
    100M100P100Q105942F
  • License MIT

DOCX document comparison and HTML conversion in the browser using WebAssembly

Package Exports

  • docxodus
  • docxodus/react

Readme

Docxodus

DOCX document comparison and HTML conversion in the browser using WebAssembly.

Docxodus brings professional-grade document comparison (redlining) to JavaScript applications. Compare two Word documents and get tracked changes, or convert DOCX files to HTML - all running entirely in the browser with no server required.

Features

  • Document Comparison: Compare two DOCX files and generate a redlined document with tracked changes
  • HTML Conversion: Convert DOCX documents to HTML for display in the browser
  • Revision Extraction: Get structured data about all revisions in a compared document
  • 100% Client-Side: All processing happens in the browser using WebAssembly
  • React Hooks: Ready-to-use hooks for React applications
  • TypeScript Support: Full type definitions included

Installation

npm install docxodus

Quick Start

Basic Usage

import { initialize, convertDocxToHtml, compareDocuments } from 'docxodus';

// Initialize the WASM runtime (call once at app startup)
await initialize('/path/to/wasm/');

// Convert DOCX to HTML
const html = await convertDocxToHtml(docxFile);

// Compare two documents
const redlinedDocx = await compareDocuments(originalFile, modifiedFile, {
  authorName: 'Reviewer'
});

React Usage

import { useDocxodus, useConversion, useComparison } from 'docxodus/react';

function DocumentViewer() {
  const { isReady, isLoading, error, convertToHtml } = useDocxodus('/wasm/');
  const [html, setHtml] = useState('');

  const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file && isReady) {
      const result = await convertToHtml(file);
      setHtml(result);
    }
  };

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <input type="file" accept=".docx" onChange={handleFile} />
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </div>
  );
}

Using the Comparison Hook

import { useComparison } from 'docxodus/react';

function DocumentComparer() {
  const {
    html,
    isComparing,
    error,
    compareToHtml,
    downloadResult
  } = useComparison('/wasm/');

  const handleCompare = async (original: File, modified: File) => {
    await compareToHtml(original, modified, { authorName: 'Legal Team' });
  };

  return (
    <div>
      {isComparing && <p>Comparing...</p>}
      {error && <p>Error: {error.message}</p>}
      {html && <div dangerouslySetInnerHTML={{ __html: html }} />}
      <button onClick={() => downloadResult('comparison.docx')}>
        Download Redlined DOCX
      </button>
    </div>
  );
}

API Reference

Core Functions

initialize(basePath?: string): Promise<void>

Initialize the WASM runtime. Must be called before using any other functions.

convertDocxToHtml(document: File | Uint8Array, options?: ConversionOptions): Promise<string>

Convert a DOCX document to HTML.

import { CommentRenderMode } from 'docxodus';

interface ConversionOptions {
  pageTitle?: string;           // HTML document title
  cssPrefix?: string;           // CSS class prefix (default: "docx-")
  fabricateClasses?: boolean;   // Generate CSS classes (default: true)
  additionalCss?: string;       // Extra CSS to include
  commentRenderMode?: CommentRenderMode;  // How to render comments (default: Disabled)
  commentCssClassPrefix?: string;         // CSS prefix for comments (default: "comment-")
}
Comment Render Modes

Control how Word document comments are rendered in HTML output:

import { convertDocxToHtml, CommentRenderMode } from 'docxodus';

// Don't render comments (default)
const html = await convertDocxToHtml(docxFile, {
  commentRenderMode: CommentRenderMode.Disabled
});

// Render as footnotes with bidirectional links
const htmlEndnote = await convertDocxToHtml(docxFile, {
  commentRenderMode: CommentRenderMode.EndnoteStyle
});

// Render as inline tooltips (title attribute + data attributes)
const htmlInline = await convertDocxToHtml(docxFile, {
  commentRenderMode: CommentRenderMode.Inline
});

// Render in a side margin column (CSS flexbox layout)
const htmlMargin = await convertDocxToHtml(docxFile, {
  commentRenderMode: CommentRenderMode.Margin
});
Mode Value Description
Disabled -1 Don't render comments (default)
EndnoteStyle 0 Comments at document end with [1] style links
Inline 1 Tooltips via title and data-comment attributes
Margin 2 Side column using CSS flexbox

compareDocuments(original, modified, options?): Promise<Uint8Array>

Compare two DOCX documents and return a redlined DOCX with tracked changes.

interface CompareOptions {
  authorName?: string;     // Author name for revisions (default: "Docxodus")
  detailThreshold?: number; // 0.0-1.0, lower = more detailed (default: 0.15)
  caseInsensitive?: boolean; // Case-insensitive comparison (default: false)
}

compareDocumentsToHtml(original, modified, options?): Promise<string>

Compare documents and return the result as HTML.

getRevisions(document: File | Uint8Array): Promise<Revision[]>

Extract revision information from a compared document.

import { getRevisions, RevisionType, isInsertion, isDeletion } from 'docxodus';
import type { Revision } from 'docxodus';

// RevisionType enum - the only two types returned by the comparison engine
enum RevisionType {
  Inserted = "Inserted",  // Text or content that was added
  Deleted = "Deleted",    // Text or content that was removed
}

// Revision interface with full documentation
interface Revision {
  /** Author who made the revision (may be empty string if not specified) */
  author: string;
  /** ISO 8601 date string (e.g., "2024-01-15T10:30:00Z"), may be empty */
  date: string;
  /** Type of revision - "Inserted" or "Deleted" */
  revisionType: RevisionType | string;
  /** Text content (newline for paragraph breaks, empty for images/equations) */
  text: string;
}

// Helper functions for type-safe filtering
const revisions = await getRevisions(comparedDoc);
const insertions = revisions.filter(isInsertion);
const deletions = revisions.filter(isDeletion);

// Or use the enum directly
revisions.forEach(rev => {
  if (rev.revisionType === RevisionType.Inserted) {
    console.log(`${rev.author} added: "${rev.text}"`);
  } else if (rev.revisionType === RevisionType.Deleted) {
    console.log(`${rev.author} removed: "${rev.text}"`);
  }
});

React Hooks

useDocxodus(wasmBasePath?: string)

Main hook providing all Docxodus functionality.

Returns:

  • isReady: boolean - Whether WASM is loaded
  • isLoading: boolean - Whether WASM is loading
  • error: Error | null - Initialization error
  • convertToHtml() - Convert DOCX to HTML
  • compare() - Compare documents
  • compareToHtml() - Compare and get HTML
  • getRevisions() - Get revision list

useConversion(wasmBasePath?: string)

Simplified hook for DOCX to HTML conversion with state management.

useComparison(wasmBasePath?: string)

Simplified hook for document comparison with state management.

Hosting WASM Files

The WASM files need to be served from your web server. After building:

  1. Copy the contents of dist/wasm/ to your public directory
  2. Pass the path to initialize() or the React hooks

Example directory structure:

public/
  wasm/
    _framework/
      dotnet.js
      dotnet.native.wasm
      ... (other framework files)
    main.js

Bundle Size

Component Size (uncompressed) Size (Brotli)
dotnet.native.wasm ~8 MB ~3 MB
Managed assemblies ~15 MB ~5 MB
Total ~37 MB ~10-12 MB

The WASM files are loaded on-demand and cached by the browser.

Browser Support

  • Chrome 89+
  • Firefox 89+
  • Safari 15+
  • Edge 89+

Requires WebAssembly SIMD support.

License

MIT

Credits

Built on Docxodus, a .NET library for document manipulation based on OpenXML-PowerTools.