On This Page
Fluent components
Mateu supports both declarative UI and fluent components.
👉 You can mix both approaches freely.
Even inside a declarative page, you can include any component that implements:
io.mateu.uidl.fluent.Component
Mixing declarative and fluent
A page does not have to be fully declarative or fully fluent.
You can keep the simplicity of declarative UI for state and actions, and introduce fluent components only where you need more control.
Example: declarative only
package io.mateu.mdd.demoadminpanel.infra.in.ui;
import io.mateu.uidl.StyleConstants;
import io.mateu.uidl.annotations.Button;
import io.mateu.uidl.annotations.Style;
import io.mateu.uidl.annotations.UI;
@UI("/simple")
@Style(StyleConstants.CONTAINER)
public class SimplePage {
String name;
@Button
void greet() {}
}

This is a fully declarative page:
namebecomes a text inputgreet()becomes a button- layout and rendering are inferred automatically
Example: declarative + fluent
package io.mateu.mdd.demoadminpanel.infra.in.ui;
import io.mateu.uidl.StyleConstants;
import io.mateu.uidl.annotations.Button;
import io.mateu.uidl.annotations.FormLayout;
import io.mateu.uidl.annotations.Style;
import io.mateu.uidl.annotations.UI;
import io.mateu.uidl.data.Avatar;
import io.mateu.uidl.data.Chart;
import io.mateu.uidl.data.ChartData;
import io.mateu.uidl.data.ChartDataset;
import io.mateu.uidl.data.ChartOptions;
import io.mateu.uidl.data.ChartType;
import io.mateu.uidl.data.HorizontalLayout;
import io.mateu.uidl.fluent.Component;
import java.util.List;
@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() {}
}

This page is still declarative, but it embeds fluent components directly.
The stats field is a fluent Component, so Mateu renders that custom UI block inside the page.
This is the key idea:
- declarative → state, actions, standard structure
- fluent → custom composition where needed
Mental model
- declarative → structure, state, behavior
- fluent → fine-grained UI composition
- both → same rendering engine
When to use fluent components
Use fluent components when:
- you need custom layouts
- you want reusable UI blocks
- you need more control than annotations provide
- you want to introduce charts, cards, avatars, or other richer components inside a standard page
Stay fully declarative when:
- forms are simple
- CRUD is standard
- inferred rendering is enough
Categories of components
Layout and structure
- VerticalLayout
- HorizontalLayout
- FormLayout
- SplitLayout
- AccordionLayout
- TabLayout
- BoardLayout
- CarouselLayout
- MasterDetailLayout
- Scroller
- Container
- FullWidth
Forms and inputs
- Form
- FormField
- FormItem
- FormRow
- FormSection
- FormSubSection
- CustomField
- Button
- MessageInput
Data display
- Grid
- Listing
- VirtualList
- Directory
- Card
- Details
- KPI
- Badge
- Chart
- Markdown
- Text
- Image
- Avatar
- AvatarGroup
Navigation and app structure
- App
- Page
- MenuBar
- Breadcrumbs
- Anchor
- Tab
Feedback and overlays
- Dialog
- ConfirmDialog
- Notification
- Tooltip
- Popover
- ProgressBar
- CookieConsent
Rich / specialized
- Bpmn
- Map
- MicroFrontend
- Element
- Icon
State and infrastructure
- State
- AppState
- Data
- AppData
- FutureComponent