Skip to content

Page types

Every page of a Mateu app belongs to one of six page types. The type is a business decision (what is the user doing on this screen?), not a layout decision, and it is the first thing to settle for each screen. The names follow the families of the Oracle Redwood page templates, the clearest public taxonomy for business UIs, and every page carries its type on the wire as pageType.

TypeThe user is…Typical templatesDefault width
Collectionfinding things in a set: searching, listing, opening, editingCRUD, smart search, to-do list, calendar, collection detail, data managementfrom the content
Detaillooking at one thing in depthitem overview, general overview, foldout, Gantt pagefrom the content
Formcompleting one task: some fields, one actioncreate & edit, advanced create & edit, task-centric pagefixed
Processmoving through steps toward a resultwizard (bar, steps, rail), guided importfixed
Dashboardmonitoring numbers to decide what needs attentiondashboardfrom the content
Landingarriving: orientation and ways inwelcome, hero searchfixed

To pick a type, ask at most three questions, in order:

  1. Many records or one? Many → collection. One → detail.
  2. Is the user changing something? In one shot → form. Across steps → process.
  3. Neither? Watching numbers → dashboard. It’s the front door → landing.

Then pick the template within the type with the decision guide, Choosing a page template, and see every template with its demo route in Page templates.

A page’s type is resolved in this order:

  1. Declared. In Java @PageTemplate(PageType.DASHBOARD), in C# [PageTemplate(PageType.Dashboard)], in Python @page_template(PageType.DASHBOARD). In YAML, a definition declares it with the pageType: key (for example pageType: dashboard), using the same six names as the wire.
  2. From the archetype. Each archetype belongs to one type (the tables below say which).
  3. From the shape. A Listing or CRUD is a collection; a view with MetricCard fields is a dashboard; a view made only of Button and component fields that @AutoPage composes as a welcome page is a landing; anything else is a form.

Inference goes one step further for the two shapes Mateu can derive entirely: with @AutoPage, a class of MetricCard fields is composed as a dashboard, and a class of Button fields and panels as a welcome page. For other shapes Mateu only advises: a plain form that looks like a known template (a selectable list next to a detail pane, for instance) logs a one-time hint naming the archetype. Below the type, @AutoLayout infers sections, tabs and widgets.

What the type changes:

  • the page’s default width (below);
  • the data-page-type attribute on the rendered page, a hook for stylesheets;
  • a shared vocabulary for docs, conformance tests and the IDE page templates.

Width is the first parameter of every page template:

WidthWhat it doesGood for
Fixedcaps the content column at 1408 px and centres ittext and forms, which get harder to read when too wide
Full widthfluid, keeping 24 px side guttersdense grids and tables
Edge to edgeno side marginscanvases: Gantt, planning board, foldout, map

Resolution order: a declaration (@PageWidth(FULL_WIDTH), C# [PageWidth(PageWidthStyle.FullWidth)], Python @page_width(PageWidth.FULL_WIDTH), or the PageWidthSupplier hook) wins. Otherwise form, process and landing pages are fixed, and collection, detail and dashboard pages are inferred from their content: full-bleed canvases (Gantt, planning board, kanban, BPMN, map) → edge to edge, a dense or inline-editable CRUD → full width, anything else → fixed. Three archetypes declare theirs: Foldout and GanttPage are edge to edge, DataManagement is full width.

In YAML, the IDE’s New › Mateu › Page… wizard writes the chosen width into the root layout’s style (see IDE tooling).


For: finding, comparing and acting on records in a set.

Anatomy: page header with the primary action (New), the smart search bar (free text + typed filter chips + saved views), the results (table, list, cards, tree or master-detail), pagination, and selection-driven bulk actions. A row opens the record, or edits it in a drawer.

Components you meet here: Listing, GridColumn, the filter bar, Badge/status columns, EmptyState, TaskQueue, Calendar, Kanban.

TemplateArchetype or pieceDetail
Create & edit (CRUD)AutoCrud<T> / Crud<…>Create and edit
Listing with capabilitiesListing<Row> + Searchable, Filterable, Navigable, Editable, Creatable, DeletableCapability listings
Smart search pageSmartSearchPage<Filters, Row>Smart search
To-do listTodoList<Row>To-do list
CalendarCalendarPageCalendar
Collection detailCollectionDetail<Row>Collection detail
Data management (grid ⇄ Gantt)DataManagementData management
Split view, tree@SplitCrud, GridLayout.treeSplit view, Tree CRUD

Listing behaviours worth knowing: filters and listing, bulk actions, saved views, totals and grouping, column chooser, inline editing, guided import.

# specs/ui/orders.yaml: rows from a named source; a row opens the record
type: Listing
title: Orders
searchable: true
rowsSource:
ref: orders
rowRoute: orders/${row.id}
toolbar:
- {type: Button, label: New, actionId: new, buttonStyle: primary}
filters:
- {type: FormField, id: status, label: Status, dataType: string, stereotype: select,
options: [{value: open, label: Open}, {value: closed, label: Closed}]}
columns:
- {type: GridColumn, id: customerName, label: Customer, sortable: true}
- {type: GridColumn, id: total, label: Total, dataType: money, align: end}
- {type: GridColumn, id: status, label: Status, dataType: status}

A CRUD listing: search, toolbar, rows

For: understanding one record in depth, and moving to its peers.

Anatomy: a rich page header (title, status badges, KPIs, timestamp, peer navigation or a record switcher), then the record’s information grouped into panels: a sticky key-info panel and tabs, property cards, lateral fold-out panels, or a canvas with a docked detail.

Components you meet here: EntityHeader, Card with property-list sections, TabLayout, FoldoutLayout, ContentLayout (main + aside), Timeline, Gantt.

TemplateArchetype or pieceDetail
Item overviewItemOverviewItem overview
General overviewGeneralOverview<Row>General overview
FoldoutFoldoutFoldout
Gantt pageGanttPageGantt
Master-detail, editable viewMasterDetailView, EditableView / AutoEditableViewEditable view, master-detail
Waterfall detailcomposition: EntityHeader + full-width cardsWaterfall detail
Detail without leaving the pagea read-only DrawerDrawer
# Key information on the left, the rest of the record in tabs on the right
type: HorizontalLayout
content:
- type: Card
style: "flex: 0 0 22rem; position: sticky; top: 0;"
title: {type: Text, text: Ergonomic chair EC-200}
content:
type: FormLayout
content:
- {type: FormField, id: sku, label: SKU, dataType: string, readOnly: true, propertyRow: true}
- {type: FormField, id: price, label: Price, dataType: money, readOnly: true, propertyRow: true}
- type: TabLayout
tabs:
- {type: Tab, label: Details, active: true, content: {type: Text, text: Details go here}}
- {type: Tab, label: History, content: {type: Text, text: History goes here}}

An item overview: key-info panel and tabs

For: completing one task: enter or change some fields and run one action.

Anatomy: page header (title, perhaps a timestamp and KPIs), the fields grouped into sections, validation messages at the field, and the closing actions in the footer (Cancel, Save). Long forms add a sticky section index (@Toc) or a contextual side panel (@Aside).

Components you meet here: Form, FormLayout, FormField with its stereotypes, FormSection, Notice, Button.

TemplateArchetype or pieceDetail
Create & edita plain @UI class, or the forms of an AutoCrudCreate and edit
Advanced create & editsections + @Toc or @Aside, header extrasAdvanced create & edit
Task-centric pageone task, one primary actionTask-centric page
Partial formsfolded or optional groupsPartial forms
type: Form
title: New customer
content:
- type: FormLayout
content:
- {type: FormField, id: name, label: Name, dataType: string, required: true}
- {type: FormField, id: email, label: Email, dataType: string, stereotype: email}
- {type: FormField, id: notes, label: Notes, dataType: string, stereotype: textarea, colspan: 2}
buttons:
- {type: Button, label: Cancel, actionId: cancel, buttonStyle: secondary}
- {type: Button, label: Save, actionId: save, buttonStyle: primary}

Bind it to a route with a view model that handles save, or give it an actions: entry with a restAction for a page with no backend.

A form: fields, validation and footer buttons

For: reaching a result through ordered steps: onboarding, check-in, an import, an end-of-day run.

Anatomy: the step’s form, a progress indicator (a bar, numbered steps, or a sticky side rail with a current | total counter), Back / Next, the completion action on the last input step, and a read-only result step.

Components you meet here: ProgressBar, ProgressSteps, Form, Notice, and for long jobs the long-running job progress.

TemplateArchetype or pieceDetail
Step-by-step / guided processWizard + @WizardProgress(STEPS | RAIL)Wizard
Guided importImportWizard<Row>Guided import
A process inside a drawerDrawer + an embedded wizardGuided process drawer
# One definition per step (signup/1, signup/2…), each routed; Next navigates to the next step
type: Form
title: Sign up
content:
- {type: Text, text: "Step 1 of 3 — Your details", size: s}
- {type: ProgressBar, value: 0.33}
- type: FormLayout
content:
- {type: FormField, id: fullName, label: Full name, dataType: string, required: true}
- {type: FormField, id: email, label: Email, dataType: string, stereotype: email, required: true}
buttons:
- {type: Button, label: Next, actionId: next, buttonStyle: primary}
actions:
- id: next
steps:
- {type: Navigate, route: signup/2}

A wizard step with progress and Next

For: seeing at a glance what needs attention, and drilling in.

Anatomy: a band of KPI cards (each optionally clickable), then a responsive grid of titled tiles (charts, short lists, notes). No form, no footer.

Components you meet here: Scoreboard, MetricCard, DashboardLayout, DashboardPanel, Chart, TrendChart, Stat, Meter, Funnel, Heatmap.

TemplateArchetype or pieceDetail
DashboardDashboard, or a class of MetricCard fields with @AutoPageDashboard
type: VerticalLayout
content:
- {type: Text, text: Sales, container: h2}
- type: Scoreboard
metrics:
- {type: MetricCard, title: Revenue, value: "1.2M", unit: EUR, trend: up, trendLabel: +8% vs last month}
- {type: MetricCard, title: Orders, value: "3,480", trend: up, trendLabel: +3%}
- type: DashboardLayout
columns: 2
items:
- {type: DashboardPanel, title: Sales by region, content: {type: Text, text: Put a Chart here}}
- {type: DashboardPanel, title: Latest orders, content: {type: Text, text: Put a Listing here}}

A dashboard: KPI band and tiles

For: the front door of an app or a workflow: orientation and a few ways in.

Anatomy: a hero (title, subtitle, optional image or tone) with calls to action, then highlight tiles; or a hero hosting a search with results as cards.

Components you meet here: HeroSection, Button, DashboardLayout, FeatureGrid, CalloutCard, Listing as cards.

TemplateArchetype or pieceDetail
WelcomeWelcome, or @WelcomeBanner on any pageWelcome page
Hero searchHeroSearch<Filters, Row>Hero search
type: VerticalLayout
content:
- type: HeroSection
title: Front desk
subtitle: Everything you need, one click away.
centered: true
content:
- {type: Button, label: Start check-in, actionId: startCheckin, buttonStyle: primary}
- type: DashboardLayout
columns: 3
items:
- {type: DashboardPanel, title: Arrivals, content: {type: Text, text: Today's arrivals.}}
- {type: DashboardPanel, title: Departures, content: {type: Text, text: Today's departures.}}

A welcome page: hero with calls to action and tiles


The templates are a starting point, not a cage. Every archetype is a composition of ordinary components, so the same pieces compose freely in a page of your own (ContentLayout, EntityHeader, HeroSection, TaskQueue, Card…), and a page you compose still declares its type with @PageTemplate or pageType:. When even composition is not enough, see Extending the UI.