Developer Guide

ODT to HTML in JavaScript

Convert .odt files to HTML in Node.js — pure JavaScript, no LibreOffice required. odf-kit reads .odt files and outputs clean, semantic HTML with headings, tables, lists, and inline formatting preserved.

The problem with ODT-to-HTML in JavaScript

The JavaScript ecosystem has mammoth for .docx-to-HTML conversion. For .odt files, the options have been far worse: odt2html was abandoned in 2016, odt.js is limited and unmaintained, and most solutions fall back to shelling out to LibreOffice headless — which requires LibreOffice to be installed on the server, adds significant startup latency, and doesn't work in serverless or edge environments.

odf-kit fills this gap. The odf-kit/reader module is a pure JavaScript ODT parser that reads .odt files directly, with no native dependencies, no LibreOffice, and no subprocess calls.

Installation

$ npm install odf-kit

Requires Node.js 22 or later. ESM only — use import, not require.

Quick start: ODT to HTML in one line

import { odtToHtml } from "odf-kit/reader";
import { readFileSync } from "fs";

const bytes = new Uint8Array(readFileSync("document.odt"));
const html = odtToHtml(bytes);

// html is a complete HTML document string:
// <!DOCTYPE html><html><head>...</head><body>...</body></html>

Fragment output (embed in an existing page)

Pass { fragment: true } to get just the body content without the HTML document wrapper — useful when embedding output into an existing page:

import { odtToHtml } from "odf-kit/reader";
import { readFileSync } from "fs";

const bytes = new Uint8Array(readFileSync("document.odt"));
const fragment = odtToHtml(bytes, { fragment: true });

// Returns inner body content only:
// <h1>Title</h1><p>Body text...</p><table>...</table>

Access the document model

Use readOdt() when you need access to the structured document model — for example to extract metadata, iterate over paragraphs, or build a custom renderer:

import { readOdt } from "odf-kit/reader";
import { readFileSync } from "fs";

const bytes = new Uint8Array(readFileSync("document.odt"));
const doc = readOdt(bytes);

// Document metadata from meta.xml
console.log(doc.metadata.title);
console.log(doc.metadata.creator);
console.log(doc.metadata.creationDate);

// Structured body — array of paragraphs, headings, lists, tables
for (const block of doc.body) {
  if (block.kind === "heading") {
    console.log(`H${block.level}: `, block.spans.map(s => s.text).join(""));
  }
}

// Or convert to HTML via toHtml()
const html = doc.toHtml({ fragment: true });

What gets extracted

ElementHTML outputStatus
Paragraphs<p>
Headings (levels 1–6)<h1><h6>
Bold text<strong>
Italic text<em>
Underline<u>
Strikethrough<s>
Superscript / subscript<sup> / <sub>
Hyperlinks<a href>
Bullet lists<ul><li>
Numbered lists<ol><li>
Nested listsNested <ul>/<ol>
Tables (including merged cells)<table><tr><td> with colspan/rowspan
Document metadatatitle, creator, dates
Line breaks<br>
Named styles (bold headings, etc.)Resolved from styles.xml
ImagesRoadmap
Fonts, colors, font sizesRoadmap
Footnotes / endnotesRoadmap

Why not shell out to LibreOffice?

LibreOffice headless (libreoffice --headless --convert-to html) is the common fallback for ODT-to-HTML conversion. It works, but it comes with real costs:

odf-kit parses .odt files directly in JavaScript. No subprocess, no installed software, no startup penalty. It works anywhere Node.js runs.

Comparison with other options

OptionPure JSMaintainedTablesMetadata
odf-kit/reader Active (2026)
LibreOffice headless SubprocessPartial
odt2html (npm) Abandoned (2016)
odt.js UnmaintainedPartial

odf-kit does more than read

The same library that reads .odt files can also create them from scratch and fill existing templates with data. If you're building a document pipeline — generate, fill, or convert — odf-kit handles all three without additional dependencies.

Convert your first .odt file

$ npm install odf-kit