Skip to content

Node.js Office Library — Quick Start

The office-oxide npm package is the native Node.js binding: it dynamically loads liboffice_oxide via koffi and exposes a small, idiomatic JavaScript API. Faster and smaller than the WebAssembly build, but ships platform-specific prebuilds rather than a single portable binary.

Need a single artifact that runs in browsers, bundlers, or Node without native libraries? Use office-oxide-wasm instead.

Install

npm install office-oxide

On postinstall, the package looks for a matching prebuilt native library under node_modules/office-oxide/prebuilds/<platform>-<arch>/. If it’s missing, configure one of these before first use:

  1. OFFICE_OXIDE_LIB — absolute path to the library (e.g. /opt/office_oxide/liboffice_oxide.so).
  2. Prebuild directory — drop the library into prebuilds/<platform>-<arch>/ (e.g. linux-x64, darwin-arm64, win32-x64).
  3. System library path — install so the library is discoverable via LD_LIBRARY_PATH / DYLD_LIBRARY_PATH / PATH.

Runs on Node 18+.

Read a document

import { Document } from 'office-oxide';

const doc = Document.open('report.docx');
try {
  console.log(doc.plainText());
} finally {
  doc.close();
}

Or with TC39 explicit resource management (Node 22+):

import { Document } from 'office-oxide';

using doc = Document.open('report.docx');
console.log(doc.plainText());
// doc.close() runs automatically at scope exit

One-shot helper:

import { extractText } from 'office-oxide';
console.log(extractText('report.docx'));

Core API

import { Document } from 'office-oxide';

const doc = Document.open('file.xlsx');
try {
  console.log(doc.format);         // "xlsx"
  console.log(doc.plainText());
  console.log(doc.toMarkdown());
  console.log(doc.toHtml());
  const ir = doc.toIr();           // parsed object

  doc.saveAs('file.docx');         // target inferred from extension
} finally {
  doc.close();
}

Open from raw bytes (Uint8Array or Buffer):

import { readFileSync } from 'node:fs';
import { Document } from 'office-oxide';

const data = readFileSync('report.pptx');
using doc = Document.fromBytes(data, 'pptx');
console.log(doc.toMarkdown());

format must be 'docx' | 'xlsx' | 'pptx' | 'doc' | 'xls' | 'ppt'.

Module-level helpers:

import { extractText, toMarkdown, toHtml, detectFormat, version } from 'office-oxide';

extractText('file.docx');      // string
toMarkdown('file.pptx');       // string
toHtml('file.xlsx');           // string
detectFormat('mystery.bin');   // "docx" | ... | null
version();                     // "0.1.0"

Editing

Editable handles preserve every unmodified OPC part on save. DOCX, XLSX, and PPTX only.

import { EditableDocument } from 'office-oxide';

using ed = EditableDocument.open('template.docx');
const n = ed.replaceText('{{name}}', 'Alice');
console.log(`${n} replacements`);
ed.save('out.docx');

Set XLSX cells

import { EditableDocument } from 'office-oxide';

using wb = EditableDocument.open('budget.xlsx');

wb.setCell(0, 'A1', 'Total');   // string
wb.setCell(0, 'B1', 42.5);      // number
wb.setCell(0, 'C1', true);      // boolean
wb.setCell(0, 'D1', null);      // empty

wb.save('budget.xlsx');

sheetIndex is zero-based; cellRef uses standard spreadsheet notation.

Format-agnostic IR

using doc = Document.open('report.docx');
const ir = doc.toIr();

for (const section of ir.sections) {
  console.log(section.title);
  for (const el of section.elements) {
    // el.kind: "Heading" | "Paragraph" | "Table" | "List" | ...
  }
}

Bytes-based pipelines

import { Document } from 'office-oxide';

const res = await fetch('https://example.com/report.docx');
const data = new Uint8Array(await res.arrayBuffer());
using doc = Document.fromBytes(data, 'docx');
console.log(doc.toMarkdown());

Legacy formats

using doc = Document.open('old.xls');
doc.saveAs('modern.xlsx');

CommonJS

const { Document } = require('office-oxide');
const doc = Document.open('file.docx');
try { console.log(doc.plainText()); } finally { doc.close(); }

Errors

Failures throw OfficeOxideError carrying numeric code and operation fields:

import { Document, OfficeOxideError } from 'office-oxide';

try {
  using doc = Document.open('missing.docx');
} catch (e) {
  if (e instanceof OfficeOxideError) {
    console.error(`code=${e.code} op=${e.operation}`);
  } else {
    throw e;
  }
}
Code Meaning
0 ok
1 invalid argument
2 io error
3 parse error
4 extraction failed
5 internal error
6 unsupported format

Troubleshooting

Symptom Fix
office-oxide: failed to load native library Set OFFICE_OXIDE_LIB to the absolute path, or drop a matching prebuild into the package.
koffi: ABI mismatch Platform/arch prebuild doesn’t match this Node process — reinstall or fetch a fresh prebuild.
TypeError: data must be a Uint8Array or Buffer Document.fromBytes only accepts binary types. Use Buffer.from(base64, 'base64').
Document is closed A method ran after close() or after leaving a using scope. Open a new handle.
Legacy .doc opens but renders gibberish Encrypted Word 97 documents are not decrypted — decrypt first via LibreOffice.

See also