Crud UI Add-on provides a fluent, type-safe API to generate CRUD UIs for Java beans in Vaadin.
The main entrypoint is Crud.of(YourBean.class), which starts with sensible defaults:
- List:
GridList - Layout:
SplitCrudLayout - Form factory:
AutoGeneratedFormFactory
For questions, use the forum thread: https://vaadin.com/forum/t/crud-ui-add-on/156634/1
- Java 21+
- Vaadin 25.2.3
- Maven
Minimal CRUD with defaults (no actual backend operations are performed):
Crud<User> crud = Crud.of(User.class).build();
add(crud);CRUD wired to backend operations:
Crud<User> crud = Crud.of(User.class)
.onCreate(userService::save)
.onRead(userService::findAll)
.onUpdate(userService::save)
.onDelete(userService::delete)
.build();
add(crud);Crud<B> is a Vaadin component (Composite<VerticalLayout>) with a fluent builder API:
Crud.of(Class<B>)onCreate(Consumer<B>)onRead(Supplier<? extends Collection<B>>)onRead(DataProvider<B, ?>)onUpdate(Consumer<B>)onDelete(Consumer<B>)list(CrudListFactory<B>)layout(CrudLayoutFactory<B>)form(CrudFormFactory<B>)build()
Main contracts:
CrudLayout<B>: how list + form are arranged in the UICrudList<B>: list abstraction (selection, refresh, data provider)CrudForm<B>: form abstraction (value/read-only/validation)CrudFormFactory<B>: factory for operation-specific forms
You can wire reads using either a collection supplier or a DataProvider.
Crud<User> crud = Crud.of(User.class)
.onRead(userService::findAll)
.build();Crud<User> crud = Crud.of(User.class)
.onRead(DataProvider.fromCallbacks(
query -> userService.findByNameContainingIgnoreCase(
filterValue, query.getPage(), query.getPageSize()).stream(),
query -> (int) userService.countByNameContainingIgnoreCase(filterValue)
))
.build();Crud<User> crud = Crud.of(User.class)
.layout(SplitCrudLayout.of(User.class)
.orientation(Orientation.HORIZONTAL))
.build();Crud<User> crud = Crud.of(User.class)
.layout(DialogCrudLayout.of(User.class)
.dialogWidth("60vw")
.dialogResizable(true))
.build();Use your own configured Grid and plug it into GridList:
Grid<User> grid = new Grid<>();
grid.addColumn(User::getId).setHeader("ID");
grid.addColumn(User::getName).setHeader("Name");
grid.addColumn(User::getEmail).setHeader("Email");
Crud<User> crud = Crud.of(User.class)
.list(GridList.of(User.class).grid(grid))
.onCreate(userService::save)
.onRead(userService::findAll)
.onUpdate(userService::save)
.onDelete(userService::delete)
.build();AutoGeneratedFormFactory supports operation-specific fields and fluent field configuration.
Crud<User> crud = Crud.of(User.class)
.form(AutoGeneratedFormFactory.of(User.class)
.fields(
Field.of(User::getName, User::setName, String.class).label("Name"),
Field.of(User::getEmail, User::setEmail, String.class).label("Email"),
Field.of(User::getActive, User::setActive, Boolean.class).label("Active")
)
.fieldsFor(CrudOperation.CREATE,
Field.of(User::getName, User::setName, String.class).label("Name"),
Field.of(User::getEmail, User::setEmail, String.class).label("Email"),
Field.of(User::getPassword, User::setPassword, String.class).label("Password")
)
.useBeanValidation()
.captions("Create user", "Edit user"))
.build();Other useful methods:
additionalFieldsFor(CrudOperation, Field.Builder...)replace(propertyName, Field.Builder)
Show selected items read-only first, then edit after clicking Update:
Crud<User> crud = Crud.of(User.class)
.viewBeforeEdit(true)
.build();Crud<User> crud = Crud.of(User.class)
.createOperationVisible(true)
.updateOperationVisible(true)
.deleteOperationVisible(false)
.build();Crud<User> crud = Crud.of(User.class)
.form().newInstanceSupplier(() -> {
User user = new User();
user.setActive(true);
return user;
})
.build();TextField nameFilter = new TextField();
nameFilter.setPlaceholder("Search by name...");
Crud<User> crud = Crud.of(User.class)
.addFilterComponent(nameFilter)
.onRead(dataProvider)
.build();Customize action buttons by CRUD operation:
Crud<User> crud = Crud.of(User.class)
.button(CrudOperation.CREATE).label("New", VaadinIcon.PLUS::create)
.button(CrudOperation.UPDATE).variants(ButtonVariant.LUMO_PRIMARY)
.button(CrudOperation.DELETE).label("Remove", VaadinIcon.TRASH::create, ButtonVariant.LUMO_ERROR)
.build();Customize delete confirmation text:
Crud<User> crud = Crud.of(User.class)
.deleteConfirmation().texts(
"Delete user?",
"This operation cannot be undone.",
"Delete",
"Cancel"
)
.onDelete(userService::delete)
.build();Crud<User> crud = Crud.of(User.class)
.onSaveSuccess(saved -> Notification.show("Saved: " + saved.getName()))
.onDeleteSuccess(deleted -> Notification.show("Deleted: " + deleted.getName()))
.onOperationError(error -> Notification.show("Operation failed: " + error.getMessage()))
.build();If you are upgrading from older version of CrudUI Add-On, use this mapping:
| Legacy usage | Current usage |
|---|---|
new GridCrud<>(User.class) |
Crud.of(User.class).build() |
setFindAllOperation(...) |
onRead(...) |
setAddOperation(...) |
onCreate(...) |
setUpdateOperation(...) |
onUpdate(...) |
setDeleteOperation(...) |
onDelete(...) |
CrudListener / LazyCrudListener wiring |
Fluent onCreate/onRead/onUpdate/onDelete methods |
DefaultCrudFormFactory old API methods |
AutoGeneratedFormFactory.of(...).fields(...).fieldsFor(...).useBeanValidation() |
- Basic:
crud-ui-demo/src/main/java/org/vaadin/crudui/demo/ui/view/DefaultView.java - Advanced:
crud-ui-demo/src/main/java/org/vaadin/crudui/demo/ui/view/CustomizedView.java
Apache License 2.0