Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
H

hucre

> 编程语言
Open source

Zero-dependency spreadsheet engine. Read & write XLSX, CSV, ODS. Pure TypeScript, works everywhere.

1.5K stars0 likes1 views
WebsiteGitHub

About

Zero-dependency spreadsheet engine. Read & write XLSX, CSV, ODS. Pure TypeScript, works everywhere.




hucre

Zero-dependency spreadsheet engine.
Read & write XLSX, CSV, ODS, JSON, NDJSON, XML. Schema validation, streaming, round-trip preservation. Pure TypeScript, works everywhere.

Quick Start

bash
npm install hucre
typescript
import { readXlsx, writeXlsx } from "hucre"

// Read an XLSX file
const workbook = await readXlsx(buffer)
console.log(workbook.sheets[0].rows)

// Write an XLSX file
const xlsx = await writeXlsx({
  sheets: [
    {
      name: "Products",
      columns: [
        { header: "Name", key: "name", width: 25 },
        { header: "Price", key: "price", width: 12, numFmt: "$#,##0.00" },
        { header: "Stock", key: "stock", width: 10 },
      ],
      data: [
        { name: "Widget", price: 9.99, stock: 142 },
        { name: "Gadget", price: 24.5, stock: 87 },
      ],
    },
  ],
})

Tree Shaking

Import only what you need:

typescript
import { readXlsx, writeXlsx } from "hucre/xlsx" // XLSX only
import { parseCsv, writeCsv } from "hucre/csv" // CSV only (~2 KB gzipped)
import { readOds, writeOds } from "hucre/ods" // ODS only
import { parseJson, writeNdjson } from "hucre/json" // JSON / NDJSON
import { readXml, writeXml } from "hucre/xml" // Tabular XML

Why hucre?

vs JavaScript / TypeScript Libraries

hucre SheetJS CE ExcelJS xlsx-js-style
Dependencies 0 0* 9 0*
Bundle (gzip) 4–129 KB† ~300 KB ~500 KB ~300 KB
ESM native Yes Partial No (CJS) Partial
TypeScript Native Bolted-on Bolted-on Bolted-on
Edge runtime Yes No No No
CSP compliant Yes Yes No (eval) Yes
npm published Yes No (CDN only) Stale Yes
Read + Write Yes Yes (Pro $) Yes Yes
Styling Yes No (Pro $) Yes Yes
Cond. formatting 13 types‡ No (Pro $) Yes No
Stream read + write Yes CSV only Yes CSV only
ODS support Yes§ Yes No Yes
Round-trip Yes Partial Partial Partial
Sparklines Yes No No No
Tables Yes Yes Yes Yes
Images Yes No (Pro $) Yes No

* SheetJS removed itself from npm; must install from CDN tarball.

† Depends on what you import — hucre is fully tree-shakeable, so there is no single number. Minified + gzipped, bundled with rolldown against dist/: { parseCsv, writeCsv } from hucre/csv = 3.7 KB, { readXlsx } from hucre/xlsx = 34 KB, { readXlsx, writeXlsx } = 68 KB, the entire library (export * from "hucre") = 129 KB.

These four are measured by pnpm size and pinned in scripts/size-budget.json, so CI fails when one grows past its budget. They are in the README because they had already drifted once — the previous figures (2.3 / 32 / 64 / 114 KB) were true when written and were enforced by nothing.

‡ cellIs, expression, colorScale, dataBar, iconSet, containsText, notContainsText, beginsWith, endsWith, containsBlanks, notContainsBlanks, duplicateValues, uniqueValues — read and written. top10 and aboveAverage round-trip in their default form only (the writer emits no rank / percent / bottom or aboveAverage / equalAverage / stdDev attributes), and timePeriod rules are dropped on read.

§ Values, formulas and merges round-trip in full; cell styling covers six facets (bold, italic, size, font colour, background colour, number format). Borders, alignment, column widths, freeze panes, validation, named ranges, images and page setup are not modelled in either direction, so ODS → ODS is lossless while XLSX → ODS drops them. See What ODS carries.

vs Libraries in Other Languages

hucre (TS) openpyxl (Py) XlsxWriter (Py) rust_xlsxwriter Apache POI (Java)
Read XLSX Yes Yes No No Yes
Write XLSX Yes Yes Yes Yes Yes
Streaming Read+Write Read+Write const_memory const_memory SXSSF (write)
Charts Round-trip 15+ types 9 types 12+ types Limited
Pivot tables Read + Write (skel) Read-only No No Limited
Cond. formatting 13 types (see ‡) Yes Yes Yes Yes
Sparklines Yes Yes Yes Yes No
Formula eval No No No No Yes
Multi-format XLSX/ODS/CSV XLSX only XLSX only XLSX only XLS/XLSX
Zero dependencies Yes lxml optional No Yes No

"Read + Write (skel)" for pivot tables: hucre writes the pivot cache, layout and relationships, but not the pre-computed value cells — Excel fills those in on first open. See Pivot Tables.

Features

Reading

typescript
import { readXlsx } from "hucre/xlsx"

const wb = await readXlsx(uint8Array, {
  sheets: [0, "Products"], // Filter sheets by index or name
  readStyles: true, // Parse cell styles
  dateSystem: "auto", // Auto-detect 1900/1904
})

for (const sheet of wb.sheets) {
  console.log(sheet.name) // "Products"
  console.log(sheet.rows) // CellValue[][]
  console.log(sheet.merges) // MergeRange[]
}

sheets also accepts a predicate that runs against lightweight metadata before each worksheet body is parsed — useful for visibility-based selection without paying the I/O cost of the full read:

typescript
const wb = await readXlsx(buf, {
  sheets: (info) => !info.hidden && !info.veryHidden,
})
// info: { name, index, hidden?, veryHidden? }

Supported cell types: strings, numbers, booleans, dates, formulas, rich text, errors, inline strings.

Writing

…

A row entry may be a value or a cell object, so styling one cell does not mean naming its position again in a parallel map:

typescript
await writeXlsx({
  sheets: [
    {
      name: "Report",
      rows: [
        [{ value: "Region", style: { font: { bold: true } } }, "Revenue"],
        ["EU", { value: 12500, style: { numFmt: "$#,##0.00" } }],
        ["Total", { formula: "SUM(B2:B2)" }],
      ],
    },
  ],
})

Anything a cell carries works there — style, formula, richText, hyperlink, checkbox. cells still takes a "row,col" map, and wins where both describe the same position.

Features: cell styles, auto column widths, merged cells, freeze/split panes, auto-filter (with per-column value filters — <filters><filter val="…"/></filters>; custom/dynamic/colour criteria are not emitted), data validation, hyperlinks, images (PNG/JPEG/GIF/SVG/WebP), comments, tables, conditional formatting (all 15 rule types, with their dxf styles), named ranges, print settings, page breaks, sheet protection, workbook protection, rich text, shared/array/dynamic formulas, sparklines, textboxes, background images, number formats, hidden sheets, Excel 2024 native checkboxes, HTML/Markdown/JSON/TSV export, template engine.

Auto Column Width

typescript
const buffer = await writeXlsx({
  sheets: [
    {
      name: "Products",
      columns: [
        { header: "Name", key: "name", autoWidth: true },
        { header: "Price", key: "price", autoWidth: true, numFmt: "$#,##0.00" },
        { header: "SKU", key: "sku", autoWidth: true },
      ],
      data: products,
    },
  ],
})

Calculates optimal column widths from cell content — font-aware, handles CJK double-width characters, number formats, min/max constraints.

Data Validation

…

Hyperlinks

typescript
const buffer = await writeXlsx({
  sheets: [
    {
      name: "Links",
      rows: [["Visit Google", "Go to Sheet2"]],
      cells: new Map([
        [
          "0,0",
          {
            value: "Visit Google",
            type: "string",
            hyperlink: { target: "https://google.com", tooltip: "Open Google" },
          },
        ],
        [
          "0,1",
          {
            value: "Go to Sheet2",
            type: "string",
            hyperlink: { target: "", location: "Sheet2!A1" },
          },
        ],
      ]),
    },
  ],
})

For tabular reports, put links inline in data rows instead of a parallel cells map — keyed by the column's key. Use the link() helper (or a plain { text, hyperlink, tooltip? } object). A #-prefixed target is treated as an internal reference (#Sheet2!A1).

typescript
import { writeXlsx, link } from "hucre/xlsx"

await writeXlsx({
  sheets: [
    {
      name: "Summary",
      columns: [
        { header: "Link", key: "link" },
        { header: "ID", key: "id" },
      ],
      data: [
        { link: link("Open", "https://example.com/items/abc-123"), id: "abc-123" },
        { link: { text: "Open", hyperlink: "https://example.com/items/def-456" }, id: "def-456" },
      ],
    },
  ],
})

Streaming

Process large files row-by-row without loading everything into memory:

…

Across the formats

…
whole read whole write stream read stream write incremental writer
XLSX ✔ ✔ ✔ ✔ (multi) ✔
CSV ✔ ✔ ✔ ✔ ✔
NDJSON ✔ ✔ ✔ ✔ ✔
ODS ✔ ✔ ✔ ✔ ✔
XML ✔ ✔ ✔ ✔ —

writeOdsStream carries values, not formatting, and that follows from the format: ODF puts <office:automatic-styles> before the body, so a style first seen on row 900,000 has nowhere to be declared — the same shape as the shared-string table, which the XLSX streaming writer answers with inline strings and ODF has no equivalent for. Column widths and a header row are carried, because columns is known before the first row. writeOds remains the path for a document that needs styling.

streamXmlRows differs from readXml in one way that follows from streaming rather than from a choice: it yields the keys each row actually has. readXml pads every row to the union of all headers, which needs the whole document —

Issues· 7 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

TypeScriptcsvcsv-parseresmexcel

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言