docx vs officegen vs pptxgenjs: Office Document Generation in Node.js (2026)
TL;DR
docx is the best library for generating Word documents (.docx) in Node.js — clean TypeScript API, composable document model, no dependency on LibreOffice or Microsoft Word. pptxgenjs is the go-to for PowerPoint (.pptx) — rich slide API, charts, images, and themes. officegen is an older library that handles both DOCX and PPTX but is less maintained. For modern report generation: docx (Word) + pptxgenjs (PowerPoint). For templating existing documents: consider docxtemplater (fills templates) instead.
Key Takeaways
- docx: ~450K weekly downloads — TypeScript-first, composable, modern DOCX generation
- pptxgenjs: ~300K weekly downloads — full PowerPoint API, charts, images, themes
- officegen: ~100K weekly downloads — legacy, generates both but less maintained
- None of these require Microsoft Office — pure JavaScript .docx/.pptx file generation
- For filling templates (not generating from scratch): use
docxtemplaterinstead - Generated files open in Microsoft Office, LibreOffice, and Google Docs
docx
docx — TypeScript DOCX generation:
Basic document
import {
Document,
Paragraph,
TextRun,
HeadingLevel,
AlignmentType,
Packer,
} from "docx"
import fs from "fs"
// Build document using composable objects:
const doc = new Document({
creator: "PkgPulse",
title: "Package Health Report",
description: "Weekly npm package health overview",
sections: [
{
children: [
// Heading:
new Paragraph({
text: "Package Health Report — March 2026",
heading: HeadingLevel.HEADING_1,
}),
// Subtitle:
new Paragraph({
children: [
new TextRun({
text: "Generated by PkgPulse",
italics: true,
color: "888888",
}),
],
}),
// Body paragraph:
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun("This report summarizes the health scores for the "),
new TextRun({
text: "top 10 npm packages",
bold: true,
}),
new TextRun(" by weekly downloads as of February 2026."),
],
}),
],
},
],
})
// Save to file:
const buffer = await Packer.toBuffer(doc)
fs.writeFileSync("report.docx", buffer)
// Or: as a base64 string, Blob, or stream
const base64 = await Packer.toBase64String(doc)
const blob = await Packer.toBlob(doc) // For browser download
Tables
import {
Document,
Table,
TableRow,
TableCell,
Paragraph,
TextRun,
WidthType,
BorderStyle,
Packer,
} from "docx"
const packages = [
{ name: "react", downloads: "45M", score: 95, status: "Healthy" },
{ name: "lodash", downloads: "35M", score: 78, status: "Stable" },
{ name: "moment", downloads: "12M", score: 42, status: "Legacy" },
]
const table = new Table({
width: { size: 100, type: WidthType.PERCENTAGE },
rows: [
// Header row:
new TableRow({
tableHeader: true,
children: ["Package", "Downloads/Week", "Health Score", "Status"].map(
(header) =>
new TableCell({
children: [
new Paragraph({
children: [new TextRun({ text: header, bold: true })],
}),
],
})
),
}),
// Data rows:
...packages.map(
(pkg) =>
new TableRow({
children: [
new TableCell({ children: [new Paragraph(pkg.name)] }),
new TableCell({ children: [new Paragraph(pkg.downloads)] }),
new TableCell({ children: [new Paragraph(String(pkg.score))] }),
new TableCell({ children: [new Paragraph(pkg.status)] }),
],
})
),
],
})
const doc = new Document({
sections: [{ children: [table] }],
})
Styles and formatting
import {
Document,
Paragraph,
TextRun,
NumberingType,
UnderlineType,
PageOrientation,
Packer,
} from "docx"
const doc = new Document({
// Page setup:
sections: [
{
properties: {
page: {
orientation: PageOrientation.LANDSCAPE,
margin: {
top: 720, // twips (1 inch = 1440 twips)
bottom: 720,
left: 1440,
right: 1440,
},
},
},
children: [
// Bulleted list:
new Paragraph({
text: "Top packages this week:",
numbering: {
reference: "my-numbering",
level: 0,
},
}),
// Styled text:
new Paragraph({
children: [
new TextRun({
text: "react",
bold: true,
color: "FF8800",
font: "Courier New",
size: 24, // half-points (24 = 12pt)
}),
new TextRun({ text: " — 45M weekly downloads" }),
],
}),
// Page break:
new Paragraph({ pageBreakBefore: true }),
],
},
],
})
Express endpoint (generate on demand)
import express from "express"
import { Document, Paragraph, Packer } from "docx"
const app = express()
app.get("/report/download", async (req, res) => {
const doc = new Document({
sections: [{
children: [
new Paragraph({ text: "PkgPulse Report", heading: "Heading1" }),
new Paragraph({ text: `Generated: ${new Date().toLocaleDateString()}` }),
],
}],
})
const buffer = await Packer.toBuffer(doc)
res.setHeader(
"Content-Type",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
)
res.setHeader("Content-Disposition", "attachment; filename=report.docx")
res.send(buffer)
})
pptxgenjs
pptxgenjs — PowerPoint generation for Node.js and browsers:
Basic presentation
import pptxgen from "pptxgenjs"
const prs = new pptxgen()
// Set presentation properties:
prs.layout = "LAYOUT_WIDE" // 13.33" x 7.5"
prs.author = "PkgPulse"
prs.company = "PkgPulse Inc."
prs.title = "Package Health Report"
// Add a title slide:
const slide1 = prs.addSlide()
slide1.addText("Package Health Report", {
x: 1,
y: 1.5,
w: "80%",
h: 1.5,
fontSize: 36,
bold: true,
color: "FF8800",
align: "center",
})
slide1.addText("Q1 2026 — PkgPulse Analytics", {
x: 1,
y: 3.2,
w: "80%",
h: 0.5,
fontSize: 18,
color: "666666",
align: "center",
})
// Add background:
slide1.background = { color: "F5F5F5" }
// Save:
await prs.writeFile({ fileName: "presentation.pptx" })
// Or: const buffer = await prs.write({ outputType: "nodebuffer" })
Tables in slides
const slide2 = prs.addSlide()
slide2.addText("Top Packages by Health Score", {
x: 0.5, y: 0.3, w: 9, h: 0.6,
fontSize: 24, bold: true,
})
const tableData = [
[
{ text: "Package", options: { bold: true, fill: { color: "2D3748" }, color: "FFFFFF" } },
{ text: "Downloads", options: { bold: true, fill: { color: "2D3748" }, color: "FFFFFF" } },
{ text: "Score", options: { bold: true, fill: { color: "2D3748" }, color: "FFFFFF" } },
],
[
{ text: "react" },
{ text: "45M/week" },
{ text: "95/100", options: { color: "22C55E" } }, // Green
],
[
{ text: "lodash" },
{ text: "35M/week" },
{ text: "78/100", options: { color: "EAB308" } }, // Yellow
],
[
{ text: "moment" },
{ text: "12M/week" },
{ text: "42/100", options: { color: "EF4444" } }, // Red
],
]
slide2.addTable(tableData, {
x: 0.5,
y: 1.2,
w: 9,
colW: [3, 3, 3],
border: { type: "solid", color: "CCCCCC", pt: 1 },
rowH: 0.5,
fontSize: 14,
})
Charts
const slide3 = prs.addSlide()
slide3.addText("Download Trends — Top 5 Packages", {
x: 0.5, y: 0.2, h: 0.6, fontSize: 20, bold: true,
})
// Bar chart:
slide3.addChart(prs.ChartType.bar, [
{
name: "Downloads (Millions/week)",
labels: ["react", "lodash", "axios", "moment", "express"],
values: [45, 35, 28, 12, 10],
},
], {
x: 0.5,
y: 0.8,
w: 9,
h: 5.5,
barDir: "col",
chartColors: ["FF8800", "3B82F6", "22C55E", "EAB308", "EF4444"],
valAxisMaxVal: 50,
showValue: true,
})
Feature Comparison
| Feature | docx | pptxgenjs | officegen |
|---|---|---|---|
| Word (.docx) | ✅ | ❌ | ✅ |
| PowerPoint (.pptx) | ❌ | ✅ | ✅ |
| Excel (.xlsx) | ❌ | ❌ | ✅ |
| Tables | ✅ | ✅ | ✅ |
| Images | ✅ | ✅ | ✅ |
| Charts | ❌ | ✅ | ❌ |
| Styles/themes | ✅ | ✅ | Limited |
| TypeScript | ✅ | ✅ | ⚠️ @types |
| ESM | ✅ | ✅ | ❌ |
| Maintenance | ✅ Active | ✅ Active | ⚠️ Low |
| Browser support | ✅ | ✅ | ❌ |
When to Use Each
Choose docx if:
- Generating Word documents (.docx) — invoices, reports, letters, contracts
- TypeScript-first with a clean composable API
- You need paragraph styling, tables, headers/footers, page layout control
Choose pptxgenjs if:
- Generating PowerPoint presentations (.pptx) — slide decks, reports, charts
- You need charts directly in slides
- Marketing reports, data presentations, automated slide generation
Choose officegen if:
- You need a single library for both DOCX and PPTX (though trade-offs apply)
- Legacy codebase already using it
Consider docxtemplater instead if:
// When you want to fill existing Word templates rather than generate from scratch:
// docxtemplater takes a .docx template with {placeholders} and fills them:
// Template: "Dear {name}, your invoice total is {amount}..."
// Much simpler than building documents programmatically
// Good for: mail merge, invoice templates, letter templates
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on docx v8.x, pptxgenjs v3.x, and officegen v0.6.x.
Compare document generation and utility packages on PkgPulse →