Schema Context
Some inputs aren't part of a row: the regions to show, the reporting period, the currency, the locale, a feature flag. Declare them as the schema's context, and every table built from that schema must pass them in.
createExcelSchema<Row, Context>();
The same ctx object reaches every callback that accepts it:
| Where | Example |
|---|---|
dynamic() builders | one column per region in ctx.regions |
condition | include a column only when ctx.showFinance is true |
accessors, transform, format | convert with ctx.fxRate, format with ctx.currency |
style, badge label callbacks | highlight ctx.featuredRegion |
hyperlink | build URLs from ctx.baseUrl |
formula | reference a rate passed at export time |
| titles, headers, group headers, summary and totals labels, validation messages, link tooltips | translate with ctx.t(key) |
Declare it and use it
import { createExcelSchema } from "typed-xlsx";
type Deal = {
amount: number;
region: "EMEA" | "AMER" | "APAC";
};
type SchemaContext = {
featuredRegion: Deal["region"];
currencySymbol: string;
};
const schema = createExcelSchema<Deal, SchemaContext>()
.column("regionOnly", {
accessor: "region",
condition: ({ ctx }) => ctx.featuredRegion === "EMEA",
})
.column("amount", {
header: ({ ctx }) => `${ctx.currencySymbol} amount`,
accessor: "amount",
format: ({ ctx }) => `${ctx.currencySymbol}#,##0.00`,
style: ({ row, ctx }) => ({
font: {
bold: row.region === ctx.featuredRegion,
},
}),
})
.build();
Text callbacks run when the table receives its context, so one schema can serve several locales:
import { createExcelSchema, createWorkbook } from "typed-xlsx";
type Row = { status: string };
type Context = { t(key: string): string };
const schema = createExcelSchema<Row, Context>()
.column("status", {
accessor: "status",
header: ({ ctx }) => ctx.t("columns.status"),
summary: (summary) => [summary.label(({ ctx }) => ctx.t("totals.label"))],
})
.build();
createWorkbook()
.sheet("Exports")
.table("statuses", {
context: { t: (key) => key },
rows: [],
schema,
title: ({ ctx }) => ctx.t("reports.statuses.title"),
});
Every table must pass the context
If a schema declares a context type, every table built from it must provide context, even when select leaves out the columns that use it. The context belongs to the schema as a whole, so the check doesn't depend on the selection:
import { createExcelSchema, createWorkbook } from "typed-xlsx";
type Row = { name: string; orgs: number[] };
const schema = createExcelSchema<Row, { orgIds: number[] }>()
.column("name", { accessor: "name" })
.dynamic("memberships", (builder, { ctx }) => {
for (const id of ctx.orgIds) {
builder.column(`org-${id}`, { accessor: (row) => row.orgs.includes(id) });
}
})
.build();
createWorkbook()
.sheet("Sheet")
.table("sheet", {
rows: [],
schema,
select: { exclude: ["memberships"] },
});
Get the context type from a schema
SchemaContextOf<typeof schema> extracts the context a built schema requires, for example to type a function that prepares it:
import { createExcelSchema } from "typed-xlsx";
import type { SchemaContextOf } from "typed-xlsx";
const schema = createExcelSchema<{ name: string }, { locale: string }>()
.column("name", { accessor: "name" })
.build();
type RequiredContext = SchemaContextOf<typeof schema>;
Row Model
How one input row becomes several worksheet rows, and how refs.column(), row.series(), expansion and summaries behave on expanded reports.
Execution Model
createWorkbook() builds from rows in memory; createWorkbookStream() commits batches to a temporary spool. What each one holds in memory and when to use it.