Skip to main content

docx vs officegen vs pptxgenjs: Office Document Generation in Node.js (2026)

·PkgPulse Team

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 docxtemplater instead
  • 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

Featuredocxpptxgenjsofficegen
Word (.docx)
PowerPoint (.pptx)
Excel (.xlsx)
Tables
Images
Charts
Styles/themesLimited
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 →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.