Customizing CRUD and listings
Mateu generates a full CRUD UI from your model.
Most real applications need to refine that default UI:
- hide fields
- adjust layouts
- customize lists
- add actions
This section shows how to do that progressively, without breaking the model-driven approach.
Golden rule
Section titled “Golden rule”Customize the model first. Customize the orchestrator only when the model is not enough.
1. Start with default CRUD
Section titled “1. Start with default CRUD”@UI("/products")public class Products extends AutoCrudOrchestrator<Product> {
final ProductAdapter adapter;
public Products(ProductAdapter adapter) { this.adapter = adapter; }
@Override public AutoCrudAdapter<Product> simpleAdapter() { return adapter; }}public record Product( String id, String name, BigDecimal price, ProductStatus status) implements Identifiable {}This already gives you:
- list
- view
- edit
- create
2. Control visibility
Section titled “2. Control visibility”Hide fields depending on context.
public record Product(
@HiddenInList String id,
String name,
BigDecimal price,
@HiddenInEditor ProductStatus status
) implements Identifiable {}Common annotations
Section titled “Common annotations”@HiddenInList@HiddenInView@HiddenInEditor@ReadOnly
3. Control editing
Section titled “3. Control editing”@EditableOnlyWhenCreatingString id;@NotNullBigDecimal price;Mateu uses standard validation annotations:
@NotNull@NotEmpty@Email- etc.
4. Improve list rendering
Section titled “4. Improve list rendering”Status as badge
Section titled “Status as badge”@StatusProductStatus status;This renders the field as a visual badge instead of plain text.
Labels and display
Section titled “Labels and display”@Overridepublic String toString() { return name;}Used in:
- lists
- lookups
- references
5. Customize forms
Section titled “5. Customize forms”Layout
Section titled “Layout”@FormLayout(columns = 2)public record Product( String name, BigDecimal price, ProductStatus status) {}Grouping fields
Section titled “Grouping fields”@FormSection("General")String name;
@FormSection("Pricing")BigDecimal price;Styling
Section titled “Styling”@Style("max-width:600px;margin:auto;")public class Products {}6. Add actions
Section titled “6. Add actions”Page-level action
Section titled “Page-level action”@Buttonpublic Object publish() { return new Message("Published");}Mutating state
Section titled “Mutating state”@Buttonpublic Object discount() { price = price.multiply(BigDecimal.valueOf(0.9)); return List.of(new Message("Discount applied"), new State(this));}7. Customize list behavior
Section titled “7. Customize list behavior”At this level, customization moves to the adapter.
public class ProductAdapter extends AutoCrudAdapter<Product> {
final ProductRepository repository;
public ProductAdapter(ProductRepository repository) { this.repository = repository; }
@Override public CrudRepository<Product> repository() { return repository; }}Typical customizations:
- filtering
- sorting
- search behavior
- pagination
8. When the model is not enough
Section titled “8. When the model is not enough”Use:
Callable<?>→ dynamic UI@Route→ custom pages- embedded orchestrators → master-detail
Example:
Callable<?> stats = () -> new HorizontalLayout( new KPI("Revenue", "10k"), new KPI("Orders", "120"));9. Anti-patterns
Section titled “9. Anti-patterns”Overusing custom pages
Section titled “Overusing custom pages”Do not create custom pages too early. Start with the model and the CRUD. Add custom pages only when the model-driven approach cannot express what you need.
Putting logic in the frontend
Section titled “Putting logic in the frontend”Mateu is backend-driven. Keep logic, validation, and state in Java. The UI is a projection of the server-side model, not an independent stateful application.
10. Summary
Section titled “10. Summary”Customization in Mateu follows this progression:
- Model (annotations)
- Validation
- Layout
- Actions
- Adapter
- Custom UI (Callable / Route)
Stay in the model as long as possible.
Move to more advanced techniques only when needed.
- Listing row actions — add per-row contextual actions with
ColumnActionandColumnActionGroup - Full control with CrudOrchestrator — explicit separate models for filters, rows, views, and forms
- Golden example: Orders, Customers and Order lines — see all of these techniques applied in a realistic business UI