Typed-xlsx
Schema Builder

Summaries

Reducer-based column summaries that work in both buffered and stream mode.

Summaries are reducer-based so the exact same schema works in buffered mode and in stream mode.

The recommended public shape is the callback builder:

  • summary.cell({...}) for reducer-based value cells
  • summary.label("TOTAL", { style }) for static labels
  • summary.empty() for alignment when a summary row intentionally skips a column

A single summary cell

import { 
createExcelSchema
} from "@chronicstone/typed-xlsx";
const
schema
=
createExcelSchema
<{
amount
: number }>()
.
column
("amount", {
accessor
: "amount",
summary
: (
summary
) => [
summary
.
cell
({
init
: () => 0,
step
: (
acc
,
row
) =>
acc
+
row
.
amount
,
finalize
: (
acc
) =>
acc
,
style
: {
numFmt
: "$#,##0.00",
}, }), ], }) .
build
();

Multiple summary rows

When you return multiple cells from the builder callback, each summary index becomes its own summary row. This is how you recreate layouts like "TOTAL BEFORE VAT" followed by "TOTAL".

import { 
createExcelSchema
} from "@chronicstone/typed-xlsx";
const
schema
=
createExcelSchema
<{
amount
: number }>()
.
column
("label", {
accessor
: () => "",
summary
: (
summary
) => [
summary
.
label
("TOTAL BEFORE VAT"),
summary
.
label
("TOTAL")],
}) .
column
("amount", {
accessor
: "amount",
summary
: (
summary
) => [
summary
.
cell
({
init
: () => 0,
step
: (
acc
,
row
) =>
acc
+
row
.
amount
,
finalize
: (
acc
) =>
acc
,
}),
summary
.
cell
({
init
: () => 0,
step
: (
acc
,
row
) =>
acc
+
row
.
amount
,
finalize
: (
acc
) =>
acc
* 1.2,
style
: {
numFmt
: "$#,##0.00" },
}), ], }) .
build
();

Styled label and empty cells

import { 
createExcelSchema
} from "@chronicstone/typed-xlsx";
const
schema
=
createExcelSchema
<{
amount
: number }>()
.
column
("label", {
accessor
: () => "",
summary
: (
summary
) => [
summary
.
label
("TOTAL", {
style
: {
font
: {
bold
: true },
alignment
: {
horizontal
: "right" },
}, }),
summary
.
empty
(),
], }) .
column
("amount", {
accessor
: "amount",
summary
: (
summary
) => [
summary
.
cell
({
init
: () => 0,
step
: (
acc
,
row
) =>
acc
+
row
.
amount
,
finalize
: (
acc
) =>
acc
,
style
: {
numFmt
: "$#,##0.00" },
}),
summary
.
cell
({
init
: () => 0,
step
: (
acc
,
row
) =>
acc
+
row
.
amount
,
finalize
: (
acc
) =>
acc
* 1.2,
style
: {
numFmt
: "$#,##0.00" },
}), ], }) .
build
();

The important idea is that summaries no longer receive rows: T[]. They accumulate progressively with init, step, and finalize, which is what makes stream mode possible without holding the full dataset in memory.

Copyright © 2026 Cyprien Thao. Released under the MIT License.