Skip to content

LuisEnMarroquin/json-as-xlsx

json-as-xlsx

npm version npm downloads Test suites license

Build an Excel (.xlsx) file straight from JSON. It is a thin, typed wrapper around the SheetJS-compatible @e965/xlsx library, so it works both in the browser (downloads the file) and in Node.js (can return a buffer when configured).

You can see a live demo on any of these sites (there are several, just in case):

Features

  • 📊 Turn an array of JSON sheets into a multi-sheet workbook.
  • 🧱 Render several tables in the same sheet (opt-in, vertical or horizontal layout).
  • 🧭 Read deeply nested values ("more.phone") or compute them with a function.
  • 🎨 Per-column number, date, currency and hyperlink formatting.
  • 🖌️ Opt-in cell styling for fonts, fills, borders, alignment and number formats.
  • ⬜ Opt-in true blank cells for empty, null or missing values.
  • 📐 Automatic column widths (tunable with extraLength).
  • ↔️ Right-to-left (RTL) sheet support.
  • 🌐 Works in the browser (file download) and in Node.js (file or buffer output).
  • 🟦 Written in TypeScript — ships with type definitions.

Installation

npm install json-as-xlsx
# or
yarn add json-as-xlsx
# or
pnpm add json-as-xlsx

Usage

import xlsx from "json-as-xlsx"
// Alternative import styles:
// import { xlsx } from "json-as-xlsx"
// const xlsx = require("json-as-xlsx")

let data = [
  {
    sheet: "Employees",
    columns: [
      { label: "Name", value: "name" }, // Top level data
      { label: "Salary", value: (row) => row.salary + " USD" }, // Custom format
      { label: "Email", value: (row) => (row.contact ? row.contact.email || "" : "") }, // Run functions
    ],
    content: [
      { name: "Ada Lovelace", salary: 5000, contact: { email: "ada@example.com" } },
      { name: "Grace Hopper", salary: 6200, contact: { email: "grace@example.com" } },
    ],
  },
  {
    sheet: "Products",
    columns: [
      { label: "Product", value: "product" }, // Top level data
      { label: "Price", value: "price", format: "$#,##0.00" }, // Column format
      { label: "Stock", value: "inventory.stock", format: "#,##0" }, // Deep props and column format
    ],
    content: [
      { product: "Keyboard", price: 29.99, inventory: { stock: 1200 } },
      { product: "Monitor", price: 199.5, inventory: { stock: 340 } },
    ],
  },
]

let settings = {
  fileName: "MySpreadsheet", // Name of the resulting spreadsheet
  enableStyles: false, // Set to true to write cell styles (`s`) into the .xlsx file
  extraLength: 3, // A bigger number means that columns will be wider
  writeMode: "writeFile", // The available parameters are 'writeFile' and 'write'. This setting is optional. Useful in such cases https://docs.sheetjs.com/docs/solutions/output#example-remote-file
  writeOptions: {}, // Style options from https://docs.sheetjs.com/docs/api/write-options
  RTL: true, // Display the columns from right-to-left (the default value is false)
  writeEmptyValuesAsBlankCells: false, // Set to true so Excel treats empty, null and missing values as blank cells
}

xlsx(data, settings) // Will download the excel file

Settings

Option Type Default Description
enableStyles boolean false Write cell style objects into the .xlsx file. Only supported with bookType: "xlsx".
fileName string "Spreadsheet" Name of the resulting file (the .xlsx extension is added automatically).
extraLength number 1 Extra characters added to every auto-calculated column width.
writeMode "writeFile"/"write" "writeFile" "writeFile" downloads/writes the file; "write" returns the raw data (e.g. a Node buffer).
writeOptions object {} Passed straight to SheetJS — see write options.
RTL boolean false Render every sheet right-to-left.
writeEmptyValuesAsBlankCells boolean false Omit empty-string, null, undefined and missing-path values so Excel treats them as blank cells.

True blank cells

For backward compatibility, empty values are written as empty strings by default. Excel can count those cells as text values even when they look blank. Set writeEmptyValuesAsBlankCells: true to omit empty-string, null, undefined and missing-path values from the worksheet XML:

let data = [
  {
    sheet: "Sparse data",
    columns: [
      { label: "ID", value: "id" },
      { label: "Email", value: (row) => row.contact?.email ?? "" },
      { label: "Score", value: "score", format: "0.00" },
    ],
    content: [
      { id: "ID-101", contact: { email: "ada@example.com" }, score: 98.5 },
      { id: "ID-102", contact: { email: "" } },
      { id: "ID-103", score: null },
      { id: "ID-104" },
    ],
  },
]

xlsx(data, {
  fileName: "BlankCellSpreadsheet",
  writeEmptyValuesAsBlankCells: true,
})

Values like 0 and false are still written normally.

This option applies to both string-path columns (value: "email") and function columns (value: (row) => row.email ?? ""). Formats, hyperlinks and cell styles are applied only to cells that still have a value; blank cells remain truly blank. Sheets or multi-table entries with no content still render their headers.

Callback

If you want to inspect or post-process the workbook, pass a callback as the third argument. It receives the generated WorkBook right before it is written, so you can read or mutate it:

let callback = function (workbook) {
  console.log("Workbook ready:", workbook.SheetNames)
}

xlsx(data, settings, callback) // Will download the excel file

Use in Node.js (server-side)

Set writeOptions.type to "buffer" (or writeMode: "write") to get the file contents back instead of writing them to disk. This is handy for sending the spreadsheet over HTTP:

import xlsx from "json-as-xlsx"

const settings = {
  writeOptions: {
    type: "buffer",
    bookType: "xlsx",
  },
}

app.get("/download", (_, res) => {
  const buffer = xlsx(data, settings)
  res.writeHead(200, {
    "Content-Type": "application/octet-stream",
    "Content-Disposition": "attachment; filename=MySheet.xlsx",
  })
  res.end(buffer)
})

Column formatting

Note: Cell formatting is type based, i.e. the format type and value type must match.

If you want to use a Date format, the value must be of type Date; if you want a number format, the value must be a Number.

Column formatting can be provided in the column object, i.e.

columns: [{ label: "Income", value: "income", format: "€#,##0.00" }]

Examples

// Number formats
"$0.00" // Basic
"\£#,##0.00" // Pound
"0%" // Percentage
'#.# "ft"' // Number and text

// Date formats
"d-mmm-yy" // 12-Mar-22
"ddd" // (eg. Sat)
"dddd" // (eg. Saturday)
"h:mm AM/PM" // 1:10 PM

Hyperlinks

Use the special "hyperlink" format to turn a column's text values into clickable links:

columns: [{ label: "Website", value: "url", format: "hyperlink" }]

Cell Styling

Cell styling is disabled by default to keep the regular export path unchanged. Set enableStyles: true to write style objects into the generated .xlsx file. Styled exports only support XLSX output; if you set writeOptions.bookType, keep it as "xlsx".

You can style headers, full columns, or individual cell values:

let data = [
  {
    sheet: "Styled employees",
    columns: [
      {
        label: "Name",
        value: "name",
        headerStyle: {
          fill: { fgColor: { rgb: "21A366" } },
          font: { bold: true, color: { rgb: "FFFFFF" } },
        },
        cellStyle: {
          alignment: { wrapText: true },
        },
      },
      {
        label: "Salary",
        value: "salary",
        format: "$#,##0.00",
        cellStyle: {
          font: { italic: true },
        },
      },
    ],
    content: [
      {
        name: {
          v: "Ada\nLovelace",
          t: "s",
          s: { font: { bold: true, color: { rgb: "FF0000" } } },
        },
        salary: 5000,
      },
    ],
  },
]

xlsx(data, {
  fileName: "StyledSpreadsheet",
  enableStyles: true,
})

Supported style groups are alignment, border, fill, font, and numFmt. Column format values are also preserved as number formats when styles are enabled.

Multiple tables per sheet

By default a sheet renders a single table from its columns and content. To place several independent tables in the same sheet, provide a tables array instead — each entry has its own columns and content (with the same formatting and styling options). This is fully opt-in: sheets that don't set tables keep working exactly as before.

let data = [
  {
    sheet: "Quarter summary",
    tablesLayout: "vertical", // "vertical" (default) stacks tables; "horizontal" places them side by side
    tablesGap: 1, // blank rows (vertical) or columns (horizontal) between tables — defaults to 1
    tables: [
      {
        columns: [
          { label: "Product", value: "product" },
          { label: "Revenue", value: "revenue", format: "$#,##0.00" },
        ],
        content: [
          { product: "Keyboard", revenue: 35988 },
          { product: "Monitor", revenue: 67830 },
        ],
      },
      {
        columns: [
          { label: "Team", value: "team" },
          { label: "Expenses", value: "expenses", format: "$#,##0.00" },
        ],
        content: [
          { team: "Engineering", expenses: 42000 },
          { team: "Marketing", expenses: 18500 },
        ],
      },
    ],
  },
]

xlsx(data, { fileName: "MultiTableSpreadsheet" })

When tables is present and non-empty it takes precedence over the sheet's top-level columns/content.

Sheet option Type Default Description
tables { columns, content }[] Render multiple tables in the sheet. Each table keeps its own formatting.
tablesLayout "vertical"/"horizontal" "vertical" Stack tables top-to-bottom or place them left-to-right.
tablesGap number 1 Blank rows (vertical) or columns (horizontal) left between tables.

TypeScript

The package is written in TypeScript and ships its own type definitions. The public interfaces are exported for your convenience. The xlsx function is available as both the default export and a named export:

import xlsx, { IJsonSheet, ISettings, IColumn, IContent, ICellStyle } from "json-as-xlsx"
// or:
// import { xlsx, IJsonSheet, ISettings, IColumn, IContent, ICellStyle } from "json-as-xlsx"

const data: IJsonSheet[] = [
  /* ... */
]
const settings: ISettings = {
  /* ... */
}

xlsx(data, settings)

Examples

These examples are part of the Yarn workspace and are intended to be installed and run from the repository root.

Contributing

Contributions are welcome! Please read the contributing guide and the code of conduct before opening a pull request. To report a security issue, see the security policy.

License

MIT © LuisEnMarroquin