Getting Started
MarcusOK publishes two packages across two categories — how they relate is on the Ecosystem page. This page starts with the one decision that comes first (which package to install), then walks through a first export with the most common one.
Requirement: Node >= 22. Example commands use pnpm; npm / yarn work the same (pnpm >= 9 is only a dev requirement of this repository, not of consumers).
Which package do I need?
| I want to… | Install | Start at |
|---|---|---|
Export data to a downloadable .xlsx | @marcusok/excel-exporter | The exporter walkthrough below |
Show an uploaded .xlsx / .xlsm / .csv, read-only | @marcusok/excel-preview | Preview quick start |
Everything below walks through @marcusok/excel-exporter; the preview's quick start is linked in the table above. For the two tasks that used to be separate packages: the progress overlay is reachable through the exporter's /overlay subpath (Overlay guide), and there is no longer a standalone engine package — both business packages bundle the engine and re-export its loader. The full comparison — dependency graph, what is bundled at runtime, how versions relate — is in Package Relationships & Selection.
1. Install
pnpm add @marcusok/excel-exporterThat is the entire setup — no same-scope runtime dependencies to think about. The export engine (modern-xlsx JS glue + fflate) and the progress overlay UI are bundled into the package's dist at build time, and the WASM binary ships under the package's own exports map, so there is no engine package to wire in and no engines conflict from upstream ranges.
2. First export
import { exportExcel, StylePresets } from "@marcusok/excel-exporter";
await exportExcel({
filename: "sales-report-2026",
sheets: [
{
name: "Sales",
freezeRows: 1,
autoFilter: true,
columns: [
{ prop: "orderId", label: "Order ID", width: 18 },
{ prop: "date", label: "Date", width: 12, format: { type: "date" } },
{
prop: "amount",
label: "Amount",
width: 14,
style: StylePresets.currency,
},
{
prop: "status",
label: "Status",
width: 10,
format: {
type: "enum",
map: { paid: "Paid", pending: "Pending" },
fallback: "Unknown",
},
},
],
data: [
{
orderId: "ORD-000001",
date: "2026-07-01",
amount: 1299.99,
status: "paid",
},
],
},
],
});In the browser this triggers a download; .xlsx is appended when missing. Use download: false to receive the Blob only.
No main.ts wiring, no bundler plugins: the shipped assets (this package's own modern-xlsx.wasm and export.worker.js) are located automatically — bundlers emit them as hashed assets via the standard new URL(asset, import.meta.url) pattern, and Node reads the wasm from disk. Node / SSR environments need no browser assets and no initialization boilerplate (see Node/SSR). Self-hosted or CDN-hosted copies are the one case that needs configureWasm.
3. Next steps
- Learn how auto mode routing picks main / worker / stream
- Try different modes and row counts in the play — the same page also demos the preview package and the progress overlay
- Start on another package from the table above, or see Package Relationships & Selection to compare them