Example: Sales Report Export
Monthly sales reports are the most common admin export. This example generates 10,000 mock sales rows and demonstrates date/currency/enum formatting, currency styling, frozen header, auto filter and progress callbacks.
Mock data preview
| Order ID | Date | Region | Product | Channel | Qty | Unit Price | Amount | Status |
|---|---|---|---|---|---|---|---|---|
| ORD-000001 | 2026-07-21 | 华东 | USB-C 扩展坞 | 线上 | 18 | 789.44 | 14209.92 | pending |
| ORD-000002 | 2026-07-28 | 西南 | 显示器支架 | 线上 | 5 | 1127.47 | 5637.35 | paid |
| ORD-000003 | 2026-07-15 | 东北 | 机械键盘 | 线下 | 1 | 898.61 | 898.61 | paid |
| ORD-000004 | 2026-07-25 | 华南 | 机械键盘 | 线上 | 4 | 360.06 | 1440.24 | refunded |
| ORD-000005 | 2026-07-14 | 华东 | 机械键盘 | 线下 | 7 | 643.8 | 4506.6 | paid |
Implementation
ts
import { exportExcel, StylePresets } from "@marcusok/excel-exporter";
// rows: sales data from your business layer, fields matching the columns
// below (fetching is omitted here; the scenario uses 10k rows — the live
// demo generates mock data for the same sales scenario, with one extra
// column, unitPrice)
const result = await exportExcel({
filename: "sales-report-2026-07",
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: "region", label: "Region", width: 10 },
{ prop: "product", label: "Product", width: 18 },
{ prop: "channel", label: "Channel", width: 10 },
{ prop: "quantity", label: "Qty", width: 8 },
{
prop: "amount",
label: "Amount",
width: 14,
style: StylePresets.currency,
},
{
prop: "status",
label: "Status",
width: 10,
format: {
type: "enum",
map: { paid: "Paid", pending: "Pending", refunded: "Refunded" },
fallback: "Unknown",
},
},
],
data: rows,
},
],
onProgress: (p) => setProgress(p),
onPhase: (phase, ms) => trackPhase(phase, ms),
});Notes
- 10k rows is below the 20,000-row threshold, so the browser takes the
mainpath (main-thread Workbook, full styling); at ≥ 20,000 rows auto mode switches toworker + Workbookand keeps the main thread responsive; onProgressonly fires at the endpoints (0 and 1) on themainpath, so the progress bar jumps straight to full at 10k rows; incremental progress requires the stream path (≥ 50,000 rows, reported every 1,000 rows — the worker + Workbook path also fires just the two endpoints);StylePresets.currencyformats the amount column (thousands, 2 decimals, right-aligned);- The
enumspec maps internal status codes to readable labels with a"Unknown"fallback; freezeRows + autoFilterlets reviewers filter directly in Excel.
Try it live in the play with the sales dataset.