Skip to content

Layout Annotations

These annotations are placed on a class or field to control how its content is arranged on screen — columns, tabs, accordion panels, split panes, and more.


Renders the page fields in a responsive multi-column grid. This is the standard layout for data-entry forms.

@Retention(RetentionPolicy.RUNTIME)
public @interface FormLayout {
String theme() default "";
String style() default "";
int columns() default 2;
}
AttributeTypeDefaultDescription
columnsint2Number of columns in the grid
styleString""Inline CSS applied to the layout container
themeString""Theme variant string passed to the design system
@UI("/customers")
@FormLayout(columns = 3)
public class CustomerForm {
String firstName;
String lastName;
String email;
String phone;
LocalDate birthDate;
}

Renders the page content in a horizontal row.

public @interface HorizontalLayout {
String theme() default "";
String style() default "";
}
AttributeTypeDefaultDescription
themeString""Theme variant
styleString""Inline CSS
@UI("/summary")
@HorizontalLayout(style = "gap: 1rem;")
public class SummaryPage {
Component salesChart;
Component revenueChart;
}

Renders the page content in a vertical column.

public @interface VerticalLayout {
String theme() default "";
String style() default "";
}
AttributeTypeDefaultDescription
themeString""Theme variant
styleString""Inline CSS
@UI("/profile")
@VerticalLayout
public class ProfilePage {
Component avatar;
String bio;
}

Places on the class to wrap all fields in a tabbed container. Individual fields are assigned to tabs via @Tab.

public @interface Tabs {
String theme() default "";
String direction() default "";
String style() default "";
}
AttributeTypeDefaultDescription
themeString""Visual theme variant
directionString""Tab strip direction — "horizontal" or "vertical"
styleString""Inline CSS for the tab container

Assigns the annotated field or method to a named tab. Requires @Tabs on the enclosing class.

Target: FIELD, METHOD

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Tab {
String value() default "";
int order() default 0;
}
AttributeTypeDefaultDescription
valueString""Tab label
orderint0Display order among tabs
@UI("/account")
@Tabs
public class AccountPage {
@Tab("Profile")
String firstName;
String lastName;
@Tab("Security")
String password;
boolean mfaEnabled;
@Tab("Preferences")
String language;
String timezone;
}

Each @Tab annotation starts a new tab. Fields without @Tab fall into a default tab.


Places on the class to render all fields inside a collapsible accordion. Individual panels are configured with @AccordionPanel.

public @interface Accordion {
String style() default "";
int opened() default 0;
}
AttributeTypeDefaultDescription
styleString""Inline CSS for the accordion container
openedint0Zero-based index of the initially expanded panel

Assigns the annotated field to a named accordion panel. Requires @Accordion on the enclosing class.

public @interface AccordionPanel {
String theme() default "";
String style() default "";
String summary() default "";
boolean disabled() default false;
}
AttributeTypeDefaultDescription
summaryString""Panel header text shown in collapsed state
themeString""Visual theme variant
styleString""Inline CSS for this panel
disabledbooleanfalseWhether this panel is non-interactive
@UI("/settings")
@Accordion(opened = 0)
public class SettingsPage {
@AccordionPanel(summary = "General")
String language;
String timezone;
@AccordionPanel(summary = "Notifications")
boolean emailNotifications;
boolean smsNotifications;
}

Renders the page as a two-panel layout with a resizable divider. The first field becomes the primary panel and the second becomes the secondary panel.

public @interface SplitLayout {
String theme() default "";
String style() default "";
}
AttributeTypeDefaultDescription
themeString""Theme variant
styleString""Inline CSS
@UI("/orders")
@SplitLayout
public class OrdersPage {
Component orderList;
Component orderDetail;
}

Target: FIELD

Renders the annotated field as a master-detail layout. When a row in the master list is selected, the detail panel appears beside it. The minHeightWhenDetailVisible value sets the minimum height of the component while the detail panel is open.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface MasterDetail {
String minHeightWhenDetailVisible();
}
AttributeTypeDefaultDescription
minHeightWhenDetailVisibleStringCSS minimum height when the detail panel is open (e.g. "400px")
public class OrdersPage {
@MasterDetail(minHeightWhenDetailVisible = "500px")
List<OrderRow> orders;
}

Target: FIELD, METHOD

Groups the annotated field and all following fields under a named section heading within a form. The section ends at the next @Section annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Section {
String value();
int columns() default 1;
String style() default "";
}
AttributeTypeDefaultDescription
valueStringSection heading label (required)
columnsint1Number of columns inside this section
styleString""Inline CSS for the section container
public class CustomerForm {
@Section("Personal data")
String firstName;
String lastName;
LocalDate birthDate;
@Section(value = "Contact", columns = 2)
String email;
String phone;
String address;
}

Sets how many columns a field spans inside its containing form layout. A value of 2 makes the field stretch across two column slots.

@Retention(RetentionPolicy.RUNTIME)
public @interface Colspan {
int value();
}
AttributeTypeDefaultDescription
valueintNumber of grid columns to span (required)
@FormLayout(columns = 2)
public class ProductForm {
String name;
double price;
@Colspan(2)
List<ProductComponent> components; // stretches across both columns
}

Target: FIELD

Specifies a CSS width for this field’s column in a grid or listing. Accepts any valid CSS length value.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ColumnWidth {
String value();
}
AttributeTypeDefaultDescription
valueStringCSS width string, e.g. "150px" or "20%" (required)
record InvoiceRow(
@ColumnWidth("80px") String id,
String description,
@ColumnWidth("120px") double total
) {}

Wraps the page content in a scrollable container.

public @interface Scroller {
String direction() default "";
String style() default "";
}
AttributeTypeDefaultDescription
directionString""Scroll direction: "vertical", "horizontal", or "both"
styleString""Inline CSS for the scroller container
@UI("/feed")
@Scroller(direction = "vertical")
public class FeedPage {
Component items;
}

Target: FIELD

Customises where the detail form appears when using a master-detail or embedded CRUD. Controls the position, column count, and visual styling of the detail panel.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface DetailFormCustomisation {
FormPosition position() default FormPosition.right;
String style() default "";
String theme() default "";
int columns() default 2;
}
ValueDescription
rightDetail panel opens to the right of the master (default)
leftDetail panel opens to the left
topDetail panel opens above
bottomDetail panel opens below
modalDetail opens in a centred modal dialog
modalLeftDetail opens in a modal anchored left
modalRightDetail opens in a modal anchored right
AttributeTypeDefaultDescription
positionFormPositionrightWhere the detail panel appears
columnsint2Number of form columns in the detail panel
styleString""Inline CSS for the detail panel
themeString""Theme variant
public class Level1View {
@DetailFormCustomisation(position = FormPosition.right, columns = 1)
List<Level2Row> items;
}

Target: TYPE, FIELD, PARAMETER

Applies inline CSS to the wrapper <div> of the annotated element. Useful for controlling the outer container independently of the element’s own style.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
public @interface DivStyle {
String value();
}
AttributeTypeDefaultDescription
valueStringInline CSS string applied to the wrapping div (required)
@DivStyle("padding: 1rem; background: #f5f5f5;")
public class InfoBox {
String message;
}

MixedPage from the admin-panel demo combines @FormLayout, @Style, and a mix of data fields and fluent components on a single page:

@UI("/mixed")
@Style(StyleConstants.CONTAINER)
@FormLayout(columns = 1)
public class MixedPage {
String name;
Component stats = new HorizontalLayout(
Chart.builder()
.chartType(ChartType.doughnut)
.chartData(ChartData.builder()
.labels(List.of("Scrap", "Create release", "Deploy"))
.datasets(List.of(ChartDataset.builder()
.label("label 1")
.data(List.of(1d, 2d, 3d))
.build()))
.build())
.chartOptions(ChartOptions.builder()
.maintainAspectRatio(false)
.build())
.build(),
new Avatar("Mateu")
);
@Button
void save() {}
}

The single-column @FormLayout stacks the name text field on top of the stats component row, with the save button rendered inline below both.