Skip to content

paperjsx/json-to-xlsx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

@paperjsx/json-to-xlsx

Generate native Excel .xlsx workbooks from JSON or TypeScript. Multi-sheet output with real cells, formulas, styles, merges, conditional formatting, tables, data validation, charts, comments, and images — emitted as genuine OOXML, not CSV.

npm license

npm install @paperjsx/json-to-xlsx

Requires Node.js >=18. ESM + CJS.

Quick Start

import { SpreadsheetEngine } from "@paperjsx/json-to-xlsx";
import { writeFileSync } from "node:fs";

const buffer = await SpreadsheetEngine.render({
  meta: { title: "Revenue Report", creator: "PaperJSX" },
  sheets: [
    {
      name: "Revenue",
      rows: [
        { cells: [{ value: "Quarter" }, { value: "Revenue" }] },
        { cells: [{ value: "Q1 2026" }, { value: 420000 }] },
        { cells: [{ value: "Q2 2026" }, { value: 465000 }] },
      ],
    },
  ],
});

writeFileSync("revenue.xlsx", buffer);

Output opens natively in Excel, Numbers, Google Sheets, and LibreOffice Calc — no compatibility layer.

What You Get

  • Native .xlsx — real ECMA-376 OOXML with multiple sheets, real cells (not stringified), and full style fidelity.
  • Formulas — declarative F.sum(), F.if(), F.vlookup(), and 40+ helpers that compile to proper A1 / R1C1 references.
  • Style dedup — fonts, fills, borders, and number formats are deduplicated via a style registry before serialization. No bloat.
  • 17 style presets — ready-made named styles (currency, percent, header, alert, etc.).
  • Conditional formatting — color scales, data bars, icon sets, expression rules.
  • Tables — real Excel tables with filters and banded rows.
  • Charts — 10+ chart types linked to sheet data.
  • Comments, images, merges, data validation — first-class support.
  • Streaming outputrenderStream() chunks large sheets to keep memory bounded.
  • Excel compliance — 1,048,576-row / 16,384-column limits enforced; the Lotus 1-2-3 phantom Feb 29, 1900 leap-year bug is handled; sheet name validation matches Excel exactly (no []:*?/\\, no leading/trailing apostrophes).
  • Deterministic output — same input, byte-identical workbook.

Document Shape

import type { SpreadsheetDocument } from "@paperjsx/json-to-xlsx";

const doc: SpreadsheetDocument = {
  meta: { title, creator, subject, keywords },
  sheets: [
    {
      name: "Summary",
      columns: [{ width: 24 }, { width: 18, style: "currency" }],
      rows: [
        {
          cells: [
            { value: "Q1 2026", style: "header" },
            { value: 420000 },
          ],
        },
      ],
      merges: ["A1:B1"],
      freezePane: { row: 1, column: 0 },
    },
  ],
};

Formulas

Build formulas declaratively with the F helpers — they produce the exact A1 string Excel expects, including reference shifting when you copy a formula across a range:

import { F } from "@paperjsx/json-to-xlsx";

{
  cells: [
    { value: "Total" },
    { formula: F.sum("B2:B5") },              // =SUM(B2:B5)
    { formula: F.if(F.gt("C2", 1000), "High", "Low") }, // =IF(C2>1000,"High","Low")
    { formula: F.vlookup("A2", "Lookup!$A$2:$B$100", 2, false) },
  ],
}

40+ builders cover math, lookup, logical, text, date/time, statistical, and financial functions. In the pro tier, evaluator.ts computes cached results server-side so cells display values in Excel immediately (without requiring F9 to recalculate).

Style Presets

{ value: 15678, style: "currency" }        // $15,678.00 right-aligned
{ value: 0.342, style: "percent" }         // 34.2%
{ value: "Region", style: "header" }       // bold, background fill, border

All 17 presets are named and composable; you can also define ad-hoc styles inline:

{
  value: "Q1",
  style: {
    font: { bold: true, color: "#FFFFFF" },
    fill: { type: "solid", color: "#0F172A" },
    alignment: { horizontal: "center" },
    border: { bottom: { style: "thin", color: "#94A3B8" } },
  },
}

Conditional Formatting

{
  name: "Sales",
  rows: [ /* ... */ ],
  conditionalFormats: [
    {
      range: "B2:B100",
      type: "colorScale",
      stops: [{ value: 0, color: "#FEE2E2" }, { value: 100000, color: "#BBF7D0" }],
    },
    {
      range: "C2:C100",
      type: "expression",
      expression: "C2>100000",
      style: { font: { bold: true, color: "#166534" } },
    },
  ],
}

Streaming

import { SpreadsheetEngine } from "@paperjsx/json-to-xlsx";
import { createWriteStream } from "node:fs";

const out = createWriteStream("large.xlsx");

await SpreadsheetEngine.renderStream(doc, {
  rowChunkSize: 2000,                // 100–10,000, default 1,000
  onChunk: (chunk) => out.write(chunk),
  onEnd: () => out.end(),
});

Public API

SpreadsheetEngine.render(doc, options?)              // Uint8Array
SpreadsheetEngine.renderStream(doc, handlers)         // chunked output
SpreadsheetEngine.validateDocument(doc)               // throws ZodError if invalid
SpreadsheetEngine.renderValidated(validated, opts?)   // skip re-validation

// Formula builder
F.sum, F.if, F.and, F.or, F.not, F.vlookup, F.hlookup, F.xlookup,
F.index, F.match, F.countif, F.sumif, F.average, F.min, F.max,
F.round, F.concat, F.text, F.date, F.today, F.now, /* 40+ total */

// Utilities
cellRef.parse("B7")    // { column: 2, row: 7 }
cellRef.format(2, 7)   // "B7"
excelSerialDate(date)  // Excel date serial number

// Errors
SpreadsheetError

Full type surface in dist/index.d.ts.

Determinism

import { setDeterministicMode } from "@paperjsx/json-to-xlsx";
setDeterministicMode(true);

Freezes timestamps, relationship IDs, and style indices so the same input produces byte-identical output.

Error Handling

import { SpreadsheetError } from "@paperjsx/json-to-xlsx";

try {
  await SpreadsheetEngine.render(doc);
} catch (err) {
  if (err instanceof SpreadsheetError) {
    console.error(err.code, err.message, err.path);
  }
  throw err;
}

Validation errors include the offending cell reference (e.g. Sheet1!C7, not rows[5].cells[2]) so debugging large sheets is practical.

Excel Compliance Notes

  • Rows capped at 1,048,576; columns at 16,384.
  • Sheet names limited to 31 chars, cannot start or end with ', cannot contain []:*?/\\.
  • Dates before Dec 31, 1899 produce negative serials and have undefined Excel behavior — validate upstream if your data may include them.
  • CJK column width uses a 1.8× multiplier heuristic. Override with explicit columns[i].width for pixel-exact layout.

Upgrade to Pro

@paperjsx/json-to-xlsx-pro adds:

  • commercial / self-hosted production license
  • server-side formula evaluation (cached results so Excel renders without F9)
  • workbook repair (corrupted .xlsx → valid .xlsx)
  • quality reporting (preflight() with memory estimation + issue surface)
  • template parsing and injection (ingest existing .xlsx files as templates)
  • advanced chart features, pivot tables

The API is identical — swap the import and provide PAPERJSX_LICENSE_KEY.

Links

License

Apache-2.0. See LICENSE.

About

Generate Excel spreadsheets from JSON

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors