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.
<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.
@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 | |||
| Invoice | Customer | Issued | Amount |
| INV-0418 | Northwind Logistics | 03/07/2026 | 12,480.00 |
| INV-0419 | Brightline Studio | 11/07/2026 | 3,250.50 |
| INV-0420 | Halden & Co. | 24/07/2026 | 8,912.00 |
| INV-0421 | Meridian Foods | 06/08/2026 | 1,740.25 |
| INV-0422 | Corvo Systems | 19/09/2026 | 21,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.
// 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.
// 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() {
}
}// 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.
// 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());
}
}// 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.
// 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;
}
}# defaults for every sheet class that declares no preset or accent colour
sheetsmith:
preset: MEDIUM
accent-color: "#1F4E79"// without Spring: depend on sheetsmith-core and build the generator
Sheetsmith sheetsmith = Sheetsmith.builder().build();Up and running in three steps.
- 1
Add the starter
One dependency, auto-configured. No platform assumptions.
- 2
Annotate your class
Classes, records and inherited fields. Only annotated fields are exported.
- 3
Call generate
Pass one or more sheets, get the workbook back.