Skip to content
sheetsmith

@ExcelSheet

Marks a class as a sheet class and configures its sheet: title, preset, shared style sheets, layout options and the header and body slots.

  • Mandatory on every class passed to SheetData. A class without it violates rule V-01.
  • Not inherited. Every exported class carries its own @ExcelSheet. Superclasses that only contribute columns do not need it.
  • Every attribute is optional. With all defaults, the sheet has no title, a frozen header, auto-sized columns, no auto-filter, no frame, and no style other than the application default preset (which is NONE unless configured).

Summary of the attributes:

Attribute Type Default
title String "" (no title)
titleStyle String "" (no style)
preset TablePreset INHERIT (application default)
accentColor String "" (application default)
styleSheets Class<?>[] {}
freezeHeader boolean true
autoFilter boolean false
autoSizeColumns boolean true
outerBorder Border INHERIT (no frame)
outerBorderColor String "" (automatic colour)
header HeaderStyles @HeaderStyles (no slot set)
body BodyStyles @BodyStyles (no slot set)
Type String
Default "", meaning no title row
Allowed values any text; written as it is
Effect Adds a row above the header. The text is written in the first column and the cell is merged across all the columns of the table. Every cell of the title row receives the title style.
Interactions The title has no role: header and body slots never apply to it. It is outside the outer frame. It is frozen together with the header when freezeHeader is true. It is excluded from the auto-filter. Its style is the preset title layer (if any) followed by titleStyle. With one column, there is nothing to merge: the title stays in the single cell.
Validation None on the text itself. titleStyle requires a title (V-15).
@ExcelSheet(title = "Open invoices at 30/09/2026")
public record InvoiceRow(/* columns */) { }
Type String, the name of a named style
Default "", meaning no style
Allowed values the name of a style available to the class: declared on the class or on one of its styleSheets
Effect Applied to every cell of the title row, after the preset title layer.
Interactions Allowed only when title is set.
Validation V-15 when title is empty; V-06 when the name does not exist.
@ExcelSheet(title = "Quarterly sales", titleStyle = "title")
@ExcelStyle(name = "title", fontSize = 16, bold = Toggle.TRUE, fontColor = "#1F4E79")
public record SalesRow(/* columns */) { }
Type TablePreset
Default TablePreset.INHERIT
Allowed values INHERIT, NONE, LIGHT, MEDIUM, DARK
Effect INHERIT uses the application default preset (SheetsmithDefaults.preset, property sheetsmith.preset, NONE unless configured). NONE applies no preset regardless of the application default. LIGHT, MEDIUM and DARK apply the corresponding preset.
Interactions The preset is the lowest level of the title, header and body cascades: every declared style overrides it attribute by attribute. The colours of the preset come from the effective accent colour.
Validation None.
@ExcelSheet(preset = TablePreset.LIGHT) // always LIGHT
@ExcelSheet(preset = TablePreset.NONE) // never a preset, even if the application default is one
@ExcelSheet // the application default preset
Type String, a colour
Default "", meaning the application default accent colour (#4472C4 unless configured)
Allowed values #RRGGBB in upper or lower case, or the name of an Apache POI IndexedColors constant (see section 6.2)
Effect The colour from which the preset derives every tone. Hexadecimal values are normalised to upper case.
Interactions Has an effect only when the effective preset is not NONE.
Validation V-13 for any other non-empty value.
@ExcelSheet(preset = TablePreset.MEDIUM, accentColor = "#2E7D32")
Type Class<?>[]
Default {}
Allowed values classes annotated with @ExcelStyleSheet
Effect The named styles of the listed style sheets become available to the slots of this class, as if declared on it.
Interactions A style declared on the sheet class with the same name as a style of a style sheet replaces it entirely (no merge). Listing the same style sheet twice has no effect. The order of the list has no effect.
Validation V-09 for a listed class without @ExcelStyleSheet (its styles are then ignored, so references to them also report V-06). V-08 when two listed style sheets declare the same name.
@ExcelSheet(styleSheets = {CorporateStyles.class, FinanceStyles.class},
header = @HeaderStyles(base = "corporate-header"))

Section 8 is dedicated to shared style sheets.

Type boolean
Default true
Effect Freezes the rows up to and including the header, so they stay visible while scrolling. When the sheet has a title, the title row is frozen too. No column is frozen.
Validation None.
@ExcelSheet(freezeHeader = false)
Type boolean
Default false
Effect Adds an Excel auto-filter covering all the columns, from the header row to the last data row. With no data rows, it covers the header row only. The title is never included.
Validation None.
@ExcelSheet(autoFilter = true)
Type boolean
Default true
Effect Sizes every column without an explicit width to its content.
Interactions Columns with @ExcelColumn.width are never auto-sized. When false, columns without width keep the width chosen by the spreadsheet application.
Validation None.

How sizing works:

  1. Sizing is delegated to Apache POI, which measures the text with the fonts installed on the machine (Java AWT).
  2. On servers or containers without installed fonts, or without the AWT native libraries, that measurement can fail. sheetsmith then falls back to an estimate: the length in characters of the longest value of the column as Excel displays it (with its format applied, so dates and numbers are measured as shown), header included and title excluded, plus 2 characters, capped at 255.
  3. The time spent sizing grows with the number of rows. For large sheets, disable it and set explicit widths: see section 12.3.

A known behaviour concerns a sheet with a title and a single column: see section 12.4.

@ExcelSheet(autoSizeColumns = false)
public record LargeRow(
@ExcelColumn(header = "Id", order = 10, width = 10) long id,
@ExcelColumn(header = "Description", order = 20, width = 60) String description) { }
Type Border
Default Border.INHERIT, meaning no frame
Allowed values every Border constant (see Appendix B)
Effect Draws a frame around the header and the data rows with a single attribute: along the top of the header row, the left side of the first column, the right side of the last column, and the bottom of the last data row (or of the header row when there are no data rows). The title is never framed.
Interactions Any value other than INHERIT, NONE included, is applied to those edges: NONE therefore removes, on the edges, borders that a preset or a base style would draw. In the cascade, the frame sits above the preset, the base slots and the odd and even slots, and below the first and last row and column slots, the column slots and the column header style: a style at those levels that sets a border side overrides the frame on that side.
Validation None.

Edges set by the frame, per cell:

Cell Sides set by the frame
Header, every column top
Header, first column left
Header, last column right
Header, every column, only when there are no data rows bottom
Data, first column left
Data, last column right
Data, last row bottom
@ExcelSheet(outerBorder = Border.MEDIUM, outerBorderColor = "#1F4E79")
Type String, a colour
Default "", meaning the automatic colour (usually black)
Allowed values #RRGGBB or an IndexedColors name
Effect Colour of the frame edges. Hexadecimal values are normalised to upper case.
Interactions Has an effect only when outerBorder is set.
Validation V-13 for an invalid non-empty value.
Type HeaderStyles
Default @HeaderStyles, no slot set
Effect The named styles of the header row: see section 4.6.
Type BodyStyles
Default @BodyStyles, no slot set
Effect The named styles of the data rows at table level: see section 4.7.
@ExcelSheet(
title = "Invoice 2026/0042",
titleStyle = "title",
preset = TablePreset.LIGHT,
accentColor = "#1F4E79",
styleSheets = CorporateStyles.class,
freezeHeader = true,
autoFilter = true,
autoSizeColumns = true,
outerBorder = Border.THIN,
outerBorderColor = "#1F4E79",
header = @HeaderStyles(base = "header", lastColumn = "header-right"),
body = @BodyStyles(lastRow = "total"))
@ExcelStyle(name = "title", fontSize = 16, bold = Toggle.TRUE)
@ExcelStyle(name = "header-right", align = Align.RIGHT)
@ExcelStyle(name = "total", bold = Toggle.TRUE, borderTop = Border.DOUBLE)
public record InvoiceLine(
@ExcelColumn(header = "Description", order = 10) String description,
@ExcelColumn(header = "Quantity", order = 20, format = "0") int quantity,
@ExcelColumn(header = "Amount", order = 30, format = "#,##0.00") BigDecimal amount) {
}

In this example header is assumed to be declared on CorporateStyles.