Package Exports
- docxodus
- docxodus/react
- docxodus/worker
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 docxodusQuick 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 loadedisLoading: boolean- Whether WASM is loadingerror: Error | null- Initialization errorconvertToHtml()- Convert DOCX to HTMLcompare()- Compare documentscompareToHtml()- Compare and get HTMLgetRevisions()- 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:
- Copy the contents of
dist/wasm/to your public directory - Pass the path to
initialize()or the React hooks
Example directory structure:
public/
wasm/
_framework/
dotnet.js
dotnet.native.wasm
... (other framework files)
main.jsBundle 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.