Skip to content
sheetsmith

Basics

Complete, self-contained use cases. Package declarations and imports are omitted where obvious. All names and data are fictional.

@ExcelSheet
public record ProductRow(
@ExcelColumn(header = "SKU", order = 10) String sku,
@ExcelColumn(header = "Name", order = 20) String name,
@ExcelColumn(header = "Price", order = 30) BigDecimal price) {
}
byte[] file = Sheetsmith.builder().build()
.generate(List.of(SheetData.of("Products", ProductRow.class, products)));

Result: header in row 1, frozen; one row per product; columns sized to content; no styles (Excel defaults); prices in the General format, unless a default number format is configured.

List<SheetData<?>> sheets = List.of(
SheetData.of("Summary", SummaryRow.class, List.of(summary)),
SheetData.of("Customers", CustomerRow.class, customers),
SheetData.of("Orders", OrderRow.class, orders));
byte[] file = sheetsmith.generate(sheets);

Sheets appear in list order. All three classes are validated before anything is written, and their errors, if any, are reported together.

Map<YearMonth, List<OrderRow>> byMonth = orders.stream()
.collect(Collectors.groupingBy(order -> YearMonth.from(order.date()), TreeMap::new, Collectors.toList()));
List<SheetData<?>> sheets = byMonth.entrySet().stream()
.<SheetData<?>>map(e -> SheetData.of(e.getKey().toString(), OrderRow.class, e.getValue()))
.toList();
byte[] file = sheetsmith.generate(sheets);

YearMonth.toString() gives names such as 2026-09, which are valid sheet names. The metadata of OrderRow is computed once.

public abstract class AuditedRow {
@ExcelColumn(header = "Created by", order = 900) protected String createdBy;
@ExcelColumn(header = "Created on", order = 910, format = "dd/mm/yyyy") protected LocalDate createdOn;
public String getCreatedBy() { return createdBy; }
public LocalDate getCreatedOn() { return createdOn; }
}
@ExcelSheet
public class ContractRow extends AuditedRow {
@ExcelColumn(header = "Contract", order = 10) private String number;
@ExcelColumn(header = "Party", order = 20) private String party;
public String getNumber() { return number; }
public String getParty() { return party; }
}

The audit columns (orders 900 and 910) follow the contract columns in every subclass. Orders must be unique across the whole hierarchy. Named styles must be declared on ContractRow (or a style sheet), not on AuditedRow.