XLSX formula recalculation in Node.js

May 29, 2026 ยท View on GitHub

This page is for the backend workflow where an .xlsx file is not just a report artifact. The service imports a workbook, edits input cells, needs the new formula values immediately, and still has to export an .xlsx at the end.

That is different from simply writing formula strings into a file and waiting for Excel to calculate later.

The production shape

A realistic server-side loop looks like this:

  1. Load or generate a pricing, payout, quote, or validation workbook.
  2. Pass the bytes to @bilig/xlsx-formula-recalc.
  3. Write request inputs into known cells.
  4. Read the recalculated formula outputs before returning a response.
  5. Export the edited workbook back to .xlsx.
  6. Reimport the exported workbook in a test and verify formulas still produce the same values.

The last step matters. It catches the difference between "the in-memory model looked right" and "the workbook artifact still works after the XLSX boundary."

Install the narrow package

npm install @bilig/xlsx-formula-recalc

For a one-off CLI run without adding it to a project:

npx --package @bilig/xlsx-formula-recalc xlsx-recalc --demo --json

The demo creates a tiny workbook, edits input cells, recalculates Summary!B2, writes bilig-formula-recalc-demo.xlsx, and prints recalculationCompleted: true, expectedValueMatched: true, and excelParity: "not_proven".

For an existing workbook:

npx --package @bilig/xlsx-formula-recalc xlsx-recalc pricing.xlsx \
  --set Inputs!B2=48 \
  --set Inputs!B3=1500 \
  --read Summary!B7 \
  --out pricing.recalculated.xlsx \
  --json

The CLI writes the updated workbook and prints the values read after recalculation. Cell targets are sheet-qualified A1 references such as Inputs!B2 or 'Pricing Model'!F12.

If you do not know the output cells yet, inspect the workbook first:

npx --package @bilig/xlsx-formula-recalc xlsx-recalc pricing.xlsx --inspect --json

Inspection does not write an output workbook. It lists formula cells, reports stale cached values, and suggests --read targets so the next command proves the cells your service actually depends on.

Run the maintained example

If you want the shortest proof without cloning the repo, run the curlable XLSX recalculation proof. It creates source and edited .xlsx files in a blank folder and verifies the round trip.

From a clean clone:

git clone https://github.com/proompteng/bilig.git
cd bilig/examples/xlsx-recalculation-node
npm install
npm start

The example builds a pricing workbook, exports pricing-model-source.xlsx, passes it through @bilig/xlsx-formula-recalc, changes input cells, reads a recalculated approval decision, exports pricing-model-edited.xlsx, and reimports the edited workbook.

Expected output includes:

{
  "before": {
    "decision": "review"
  },
  "after": {
    "decision": "approved"
  },
  "checks": {
    "decisionChanged": true,
    "exportedReimportMatchesAfter": true,
    "formulasSurvivedXlsxRoundTrip": true,
    "verified": true
  }
}

The source is intentionally small enough to read in one sitting: examples/xlsx-recalculation-node/recalculate-xlsx.ts.

Minimal API boundary

Use the package API at the file boundary when the service needs a recalculated answer immediately:

import { readFile, writeFile } from 'node:fs/promises'
import { recalculateXlsx } from '@bilig/xlsx-formula-recalc'

const result = recalculateXlsx(await readFile('pricing-model-source.xlsx'), {
  fileName: 'pricing-model-source.xlsx',
  edits: [
    { target: 'Inputs!B2', value: 48 },
    { target: 'Inputs!B3', value: 1250 },
  ],
  reads: ['Summary!B7'],
})

await writeFile('pricing-model-edited.xlsx', result.xlsx)
console.log({ decision: result.reads['Summary!B7'], warnings: result.warnings })

In production, keep a narrow adapter around this boundary. Your application should know exactly which cells are inputs, which cells are outputs, and which checks prove the workbook is still valid after export.

When to keep using ExcelJS or SheetJS

Use ExcelJS or SheetJS first when the job is workbook-file manipulation: styling, rows, sheets, images, tables, streaming writes, or broad spreadsheet format interchange.

Use @bilig/xlsx-formula-recalc when the Node process must own the recalculated answer before it accepts, rejects, queues, or persists a workflow. Use @bilig/headless/xlsx directly only when you need the lower-level WorkPaper snapshot boundary.

Many services should combine the tools: use a file library for presentation details and use a formula runtime for the auditable decision path.

Trust checks before production

Before putting XLSX recalculation on a customer-critical path, add tests for:

  • the exact input cells your service writes
  • the exact output cells your service reads
  • unsupported formulas and compatibility limits
  • exported workbook reimport
  • stale cached values in source files
  • a golden workbook fixture that opens in Excel or another spreadsheet app

Bilig is not full Excel. The useful promise is narrower: a Node service can run formula-backed workbook logic, prove readback after edits, and keep a checked XLSX boundary around the parts it owns.

After the proof

If verified: true matches your Node/XLSX workflow, star the repository or bookmark Bilig so the recalculation path is easy to find again: https://github.com/proompteng/bilig/stargazers.

Watch releases if this is close to a production dependency: https://github.com/proompteng/bilig/subscription.

If a real workbook is close but blocked by a formula, external-link, import, export, or round-trip edge case, open the smallest adoption blocker you can share: https://github.com/proompteng/bilig/discussions/new?category=general.