sheetsmith
v1.0.x

Spring Boot · Apache POI · Java 17+

Spreadsheets, forged in Java.

Describe a table once with annotations, pass a list of objects, get an .xlsx file back: title, header, rows, styles, presets and formats included.

pom.xml
<dependency>
    <groupId>cloud.baldilorenzo</groupId>
    <artifactId>sheetsmith-spring-boot-starter</artifactId>
    <version>1.0.x</version>
</dependency>

What you write, and what your users open

Hover an annotation to see what it controls. Pick a preset and an accent colour to see the result.

Preset
Accent
InvoiceRow.java
@ExcelSheet(
        title = "Q3 2026 invoices",
        preset = TablePreset.MEDIUM,
        accentColor = "#1F4E79",
        autoFilter = true)
public record InvoiceRow(
        @ExcelColumn(header = "Invoice", order = 10)
        String number,
        @ExcelColumn(header = "Customer", order = 20)
        String customer,
        @ExcelColumn(header = "Issued", order = 30,
                format = "dd/mm/yyyy")
        LocalDate issued,
        @ExcelColumn(header = "Amount", order = 40,
                format = "#,##0.00")
        BigDecimal amount) {
}

// the sheet name is given when generating
sheetsmith.generate(List.of(
        SheetData.of("Invoices", InvoiceRow.class, rows)));
Q3 2026 invoices
InvoiceCustomerIssuedAmount
INV-0418Northwind Logistics03/07/202612,480.00
INV-0419Brightline Studio11/07/20263,250.50
INV-0420Halden & Co.24/07/20268,912.00
INV-0421Meridian Foods06/08/20261,740.25
INV-0422Corvo Systems19/09/202621,005.00

Everything a report needs, declared where the data lives.

01 · OUTPUT

One call. Bytes or a stream.

Pass as many sheets as the workbook needs, each with its own class. Get the file back as a byte array, or write it straight to an output stream such as an HTTP response.

ReportService.java
// one SheetData per tab: sheet name, sheet class, rows
List<SheetData<?>> sheets = List.of(
        SheetData.of("Invoices", InvoiceRow.class, invoices),
        SheetData.of("Customers", CustomerRow.class, customers));

// the whole workbook in memory
byte[] file = sheetsmith.generate(sheets);

// or written straight to a stream, such as an HTTP response:
// the stream is flushed, never closed
sheetsmith.generate(sheets, response.getOutputStream());

02 · COMPANY STYLE

Define your style once. Use it in every report.

Collect named styles in a style sheet class, then reference it from any sheet class. A style declared on the sheet class wins over one with the same name from the style sheet.

CorporateStyles.java
// named styles, defined once for the whole company
@ExcelStyleSheet
@ExcelStyle(name = "header", bold = Toggle.TRUE,
        fillColor = "#1F4E79", fontColor = "#FFFFFF")
@ExcelStyle(name = "zebra", fillColor = "#EEF3F8")
public final class CorporateStyles {

    private CorporateStyles() {
    }
}
InvoiceLine.java
// any sheet class references them by name
@ExcelSheet(
        styleSheets = CorporateStyles.class,
        header = @HeaderStyles(base = "header"),
        body = @BodyStyles(odd = "zebra"))
public record InvoiceLine(
        @ExcelColumn(header = "Description", order = 10)
        String description) {
}

03 · CONVERTERS

Any type, written the way you need.

Text, numbers, booleans, enums, LocalDate and LocalDateTime work out of the box. For any other type, write a converter. Declared as a bean, it covers every column of that type. Declared on a field, it covers that column only.

MoneyConverter.java
// declared as a bean: applies to every Money column, in every sheet
@Component
public class MoneyConverter implements CellConverter<Money> {

    @Override
    public CellValue convert(Money value, ConversionContext context) {
        return CellValue.number(value.amount().doubleValue());
    }
}
DocumentRow.java
// a plain class, not a bean: the library creates it for this column
public class UuidConverter implements CellConverter<UUID> {

    @Override
    public CellValue convert(UUID value, ConversionContext context) {
        return CellValue.text(value.toString());
    }
}

@ExcelSheet
public record DocumentRow(
        // declared on the field: applies to this column only
        @ExcelColumn(header = "Id", order = 10,
                converter = UuidConverter.class)
        UUID id,
        @ExcelColumn(header = "Title", order = 20)
        String title) {
}

04 · SETUP

Spring Boot native. Plain Java welcome.

With the starter, a ready-to-use Sheetsmith bean is auto-configured: you only inject it. Application-wide defaults, such as preset and accent colour, go in your properties. Without Spring, the core module gives you the same generator through a builder.

ReportService.java
// the starter registers a Sheetsmith bean: inject it where you need it
@Service
public class ReportService {

    private final Sheetsmith sheetsmith;

    public ReportService(Sheetsmith sheetsmith) {
        this.sheetsmith = sheetsmith;
    }
}
application.yml
# defaults for every sheet class that declares no preset or accent colour
sheetsmith:
  preset: MEDIUM
  accent-color: "#1F4E79"
Plain Java
// without Spring: depend on sheetsmith-core and build the generator
Sheetsmith sheetsmith = Sheetsmith.builder().build();

Up and running in three steps.

  1. 1

    Add the starter

    One dependency, auto-configured. No platform assumptions.

  2. 2

    Annotate your class

    Classes, records and inherited fields. Only annotated fields are exported.

  3. 3

    Call generate

    Pass one or more sheets, get the workbook back.

Get started