Iteration Layer Sheet Generation vs SheetJS: Library or API Call?

7 min read Sheet Generation

SheetJS Is Everywhere. That’s Not the Problem.

SheetJS (the xlsx npm package) is the most downloaded spreadsheet library in the JavaScript ecosystem. It handles reading and writing Excel files, CSV parsing, and format conversions. If you’ve ever Googled “generate Excel file in JavaScript,” SheetJS was the first result. It probably still is.

The problem isn’t popularity. It’s what you inherit when you use it — a confusing API surface, a free tier that strips out the features you actually need, a Pro license that costs $500+ per year, and a hard lock to JavaScript. If your backend is Python, Go, Elixir, or anything else, SheetJS isn’t an option.

The API That Fights You

SheetJS uses a data model built around workbooks, worksheets, and cell objects addressed by A1-style references. Generating a simple spreadsheet with headers and formatted data looks like this:

import XLSX from "xlsx";

const workbook = XLSX.utils.book_new();
const worksheetData = [
  ["Company", "Region", "Revenue"],
  ["Acme Corp", "North America", 48500],
  ["Globex Ltd", "Europe", 37200],
];
const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);

// Set column widths
worksheet["!cols"] = [
  { wch: 20 },
  { wch: 20 },
  { wch: 15 },
];

XLSX.utils.book_append_sheet(workbook, worksheet, "Q1 Revenue");
const buffer = XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });

That’s the free version. Notice what’s missing: no bold headers, no currency formatting, no background colors. The community edition of SheetJS doesn’t support cell styles. At all.

Want formatted cells? You need SheetJS Pro, which requires a commercial license starting at $500 per year. And even with Pro, styling a cell means mutating objects with cryptic property names:

worksheet["C2"].s = {
  numFmt: "$#,##0.00",
  font: { bold: true },
  fill: { fgColor: { rgb: "FF1A1A2E" } },
};

Every cell styled individually. s for style, numFmt as a raw Excel format string, fgColor.rgb with an alpha prefix you didn’t ask for. It’s not an API you learn once — it’s an API you look up every time.

What SheetJS Can’t Do (or Does Poorly)

Beyond the formatting paywall, SheetJS has real limitations:

  • Formulas are pass-through. You can write =SUM(B2:B4) into a cell, but SheetJS doesn’t evaluate it. The formula string goes into the file verbatim. If you export to CSV, you get the literal string =SUM(B2:B4) instead of a computed value.
  • Merged cells are manual. You set worksheet["!merges"] with an array of range objects using { s: { r: 0, c: 0 }, e: { r: 0, c: 3 } } notation. It works, but it’s another thing to get right.
  • No multiple output formats from the same model. Want XLSX and CSV from the same data? You build the worksheet once, then handle the format differences yourself. CSV won’t carry your styles, and Markdown isn’t a supported output at all.
  • JavaScript only. Your Python service, your Go microservice, your Java batch processor — none of them can use SheetJS. You either write a Node.js sidecar or find a different library for each language.

A Direct Comparison

Here’s the same task — a formatted revenue report with currency values — in both approaches.

SheetJS Pro (you host this, $500+/year license):

import XLSX from "xlsx";

const workbook = XLSX.utils.book_new();
const worksheetData = [
  ["Company", "Region", "Revenue"],
  ["Acme Corp", "North America", 48500],
  ["Globex Ltd", "Europe", 37200],
];
const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);

// Style every header cell individually
worksheet["A1"].s = {
  font: { bold: true, color: { rgb: "FFFFFFFF" } },
  fill: { fgColor: { rgb: "FF1A1A2E" } },
};
worksheet["B1"].s = {
  font: { bold: true, color: { rgb: "FFFFFFFF" } },
  fill: { fgColor: { rgb: "FF1A1A2E" } },
};
worksheet["C1"].s = {
  font: { bold: true, color: { rgb: "FFFFFFFF" } },
  fill: { fgColor: { rgb: "FF1A1A2E" } },
};

// Format currency cells
worksheet["C2"].s = { numFmt: "$#,##0.00" };
worksheet["C3"].s = { numFmt: "$#,##0.00" };

XLSX.utils.book_append_sheet(workbook, worksheet, "Q1 Revenue");
const buffer = XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });

Iteration Layer (any language, formatting included):

Request
curl -X POST https://api.iterationlayer.com/sheet-generation/v1/generate \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "xlsx",
    "styles": {
      "header": {
        "is_bold": true,
        "background_color": "#1a1a2e",
        "font_color": "#ffffff"
      }
    },
    "sheets": [
      {
        "name": "Q1 Revenue",
        "columns": [
          {
            "name": "Company"
          },
          {
            "name": "Region"
          },
          {
            "name": "Revenue"
          }
        ],
        "rows": [
          [
            {
              "value": "Acme Corp"
            },
            {
              "value": "North America"
            },
            {
              "value": 48500.00,
              "format": "currency",
              "currency_code": "USD"
            }
          ],
          [
            {
              "value": "Globex Ltd"
            },
            {
              "value": "Europe"
            },
            {
              "value": 37200.00,
              "format": "currency",
              "currency_code": "EUR"
            }
          ]
        ]
      }
    ]
  }'
Response
{
  "success": true,
  "data": {
    "buffer": "UEsDBBQAAAAIAA...",
    "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  }
}
Request
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({
  apiKey: "YOUR_API_KEY",
});

const result = await client.generateSheet({
  format: "xlsx",
  styles: {
    header: {
      is_bold: true,
      background_color: "#1a1a2e",
      font_color: "#ffffff",
    },
  },
  sheets: [
    {
      name: "Q1 Revenue",
      columns: [
        {
          name: "Company",
        },
        {
          name: "Region",
        },
        {
          name: "Revenue",
        },
      ],
      rows: [
        [
          {
            value: "Acme Corp",
          },
          {
            value: "North America",
          },
          {
            value: 48500.00,
            format: "currency",
            currency_code: "USD",
          },
        ],
        [
          {
            value: "Globex Ltd",
          },
          {
            value: "Europe",
          },
          {
            value: 37200.00,
            format: "currency",
            currency_code: "EUR",
          },
        ],
      ],
    },
  ],
});
Response
{
  "success": true,
  "data": {
    "buffer": "UEsDBBQAAAAIAA...",
    "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  }
}
Request
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")

result = client.generate_sheet(
    format="xlsx",
    styles={
        "header": {
            "is_bold": True,
            "background_color": "#1a1a2e",
            "font_color": "#ffffff",
        },
    },
    sheets=[
        {
            "name": "Q1 Revenue",
            "columns": [
                {
                    "name": "Company",
                },
                {
                    "name": "Region",
                },
                {
                    "name": "Revenue",
                },
            ],
            "rows": [
                [
                    {
                        "value": "Acme Corp",
                    },
                    {
                        "value": "North America",
                    },
                    {
                        "value": 48500.00,
                        "format": "currency",
                        "currency_code": "USD",
                    },
                ],
                [
                    {
                        "value": "Globex Ltd",
                    },
                    {
                        "value": "Europe",
                    },
                    {
                        "value": 37200.00,
                        "format": "currency",
                        "currency_code": "EUR",
                    },
                ],
            ],
        },
    ],
)
Response
{
  "success": true,
  "data": {
    "buffer": "UEsDBBQAAAAIAA...",
    "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  }
}
Request
package main

import il "github.com/iterationlayer/sdk-go"

func main() {
    client := il.NewClient("YOUR_API_KEY")

    result, err := client.GenerateSheet(il.GenerateSheetRequest{
        Format: "xlsx",
        Styles: &il.SheetStyles{
            Header: &il.CellStyle{
                IsBold:          true,
                BackgroundColor: "#1a1a2e",
                FontColor:       "#ffffff",
            },
        },
        Sheets: []il.Sheet{
            {
                Name: "Q1 Revenue",
                Columns: []il.SheetColumn{
                    {
                        Name: "Company",
                    },
                    {
                        Name: "Region",
                    },
                    {
                        Name: "Revenue",
                    },
                },
                Rows: []il.SheetRow{
                    {
                        {
                            Value: "Acme Corp",
                        },
                        {
                            Value: "North America",
                        },
                        {
                            Value:        48500.00,
                            Format:       "currency",
                            CurrencyCode: "USD",
                        },
                    },
                    {
                        {
                            Value: "Globex Ltd",
                        },
                        {
                            Value: "Europe",
                        },
                        {
                            Value:        37200.00,
                            Format:       "currency",
                            CurrencyCode: "EUR",
                        },
                    },
                },
            },
        },
    })
    if err != nil {
        panic(err)
    }
    _ = result
}
Response
{
  "success": true,
  "data": {
    "buffer": "UEsDBBQAAAAIAA...",
    "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  }
}

The SheetJS version styles each cell individually with cryptic property names. The API version declares styles once in styles.header and they apply to every header cell. Currency formatting is "format": "currency" — not a raw Excel format string you had to look up.

And that same JSON payload works from TypeScript, Python, Go, or any language that speaks HTTP. No library, no license, no JavaScript required.

When SheetJS Is the Right Choice

SheetJS wins in specific scenarios. Being honest about those matters more than pretending an API is always better.

  • You need to read and parse spreadsheets. SheetJS is primarily a parsing library. Reading uploaded Excel files, extracting data from CSVs, converting between formats — that’s its core strength. The Sheet Generation API generates files; it doesn’t parse them.
  • Offline or air-gapped environments. If your spreadsheets can’t leave your network, a cloud API isn’t an option.
  • You already have SheetJS Pro and it works. If your team has the license, knows the API, and your spreadsheet generation code is stable, there’s no reason to migrate for the sake of migrating.
  • High-volume, latency-sensitive scenarios. If you generate thousands of spreadsheets per second and need sub-millisecond response times, a local library call is faster than an HTTP round trip.

When an API Makes More Sense

For most teams, the trade-off favors the API.

  • Your backend isn’t JavaScript. Python, Go, Elixir, Ruby, Java, Rust — the API works with any language that can make HTTP requests. No Node.js sidecar.
  • You need formatting without a $500 license. Styled headers, currency formatting, background colors, bold text — all included in the API. No paywall.
  • You need multiple output formats. The same JSON payload produces XLSX, CSV, or Markdown. Change one field, get a different format. No separate code paths.
  • You don’t want to learn A1-style cell references. The API uses a positional model — columns and rows. No worksheet["C2"].s, no !merges arrays, no aoa_to_sheet.
  • You need formulas that work in CSV. The API evaluates formulas server-side for non-XLSX formats. =SUM(B2:B4) becomes 13200 in CSV output. SheetJS writes the formula string verbatim.

Get Started

Check the Sheet Generation docs for the full API reference — cell formats, style properties, formula support, and cell merging. The TypeScript and Python SDKs handle authentication and response parsing.

Iteration Layer runs on EU infrastructure (Frankfurt), which matters if your data residency requirements rule out US-hosted services.

Start building in minutes

Free trial included. No credit card required.