Skip to content

Page templates

Enterprise design systems (Oracle Redwood’s RDS toolkit is the canonical example) don’t stop at components — they standardize full-page templates: the record overview, the collection with a detail pane, the guided process, the create-and-edit drawer. Mateu covers these templates from the UI model: you extend an archetype or set a flag, and the page comes out with the template’s anatomy on every renderer that paints its components (see the Redwood column).

This page is the map. Each row names the template (RDS naming), the Mateu piece that builds it, and where to see it running. The normal way to build a UI starts here: list the screens your app needs, decide the page type and the template for each one, then declare the data — choosing the template is your call per screen (Mateu only composes it for you in two fully-derivable cases; see Choosing a page template, the decision guide).

TemplateMateu pieceDemo routeGuideRedwood
Smart Filter and SearchEvery CRUD/Listing — the smart search bar with typed filters, chips and saved views is the default listing UX/productsFilters & listing✅
Smart Search pageSmartSearchPage<Filters, Row> archetype — a standalone, search-first page: intro line + smart search bar with typed facets + results; starts empty/smart-search-demoSmart search✅
To-do listTodoList<Row> archetype — pending work as counted buckets of actionable cards; clicking a task acts on it/todo-list-demoTo-do list✅
Create and Edit — SimpleAutoCrud<T> — routed /new and /{id}/edit forms with validation, optimistic locking and dirty guard/productsCreate and edit✅
Create and Edit — Advanceda sectioned transactional form with a canonical header (save/cancel, @Timestamp, @KPI facts, peer-nav) and either @Toc (section index) or @Aside (detail slot)—Advanced create & edit🟡 no @Toc index
Create and Edit — DrawereditInDrawer() on the crud — the form slides over the listing, which never unmounts/drawer-crud-demoDrawer✅
Waterfall detailComposition — a VerticalLayout of EntityHeader + full-width Card/property-list panels telling the record top-to-bottom—Waterfall detail✅
Step-by-stepWizard + @WizardProgress(STEPS) — the classic numbered-step stepper with done/current/upcoming states/branching-wizardWizard✅ oj-train
Collection DetailCollectionDetail<Row> archetype — searchable card list + in-place detail pane/collection-detail-demoCollection detail✅
General OverviewGeneralOverview<Row> archetype — record context switcher + EntityHeader metadata strip over property cards/general-overview-demoGeneral overview✅ card contents render as atoms (a Grid is an oj-table)
Guided ProcessWizard + @WizardProgress(RAIL) — step form with the sticky lateral rail (current | total counter over the vertical step list)/branching-wizardWizard✅
Item OverviewItemOverview archetype — sticky key-info panel + tabbed detail/product-overviewItem overview✅ key panel and tabs render their components (Markdown, charts, lists…)
Foldout LayoutFoldout archetype — fixed overview + lateral fold-out panels/foldout-demoFoldout✅ oj-sp-foldout-layout
DashboardDashboard archetype — KPI scoreboard band + responsive tile grid/dashboard-demoDashboard✅ KPI band + oj-chart tiles
Welcome PageWelcome archetype — hero with CTAs + highlight tiles/welcome-demoWelcome page✅
Empty StatesEmptyState component — used by every listing and archetype automatically/empty-skeleton-demoEmpty states & skeletons✅ (Skeleton —)
Data Management (grid ⇄ Gantt)DataManagement archetype — the same data as a grid and a Gantt with a toolbar switcher (full-width). Dense editable grids use @InlineEditing + @Compact/data-management-demoData management✅ grid as oj-table, Gantt as oj-gantt
Gantt pageGanttPage archetype — edge-to-edge Gantt canvas + docked detail; clicking a bar opens the task in a drawer/gantt-page-demoGantt✅ oj-gantt; a bar click opens the task drawer
CalendarCalendarPage archetype — the full page: calendar toolbar (‹/Today/›, optional + Create) over the calendar, per-month event fetching, event click actions, month/week/day/list views, per-date labels and clickable dates/calendar-demoCalendar✅

Three notes on how to read the table:

  • Templates are backend-side. An archetype composes existing wire components, so it renders wherever those components render — on Vaadin and the native renderers (React Native, IntelliJ) without renderer work. Redwood is the exception to watch: it paints a curated set of components with Oracle’s own, and shows a visible Unsupported component placeholder for the ones it has no view for — the last column says what each template looks like there, and the Redwood component coverage table (checked in CI) says why. Styling follows each design system’s tokens.

  • Page width is a template parameter. @PageWidth(FIXED | FULL_WIDTH | EDGE_TO_EDGE) (or the PageWidthSupplier hook — Foldout declares edge-to-edge) decides how the content column is sized: capped and centered, fluid with side margins, or touching the viewport edges. When neither is set, the renderer infers it from the content (full-bleed canvases → edge-to-edge, dense datagrids → full width, anything else → fixed). On the Vaadin shell all three modes render: fixed caps the content column at 1408 px centered, full width is fluid, and edge to edge drops the shell gutters for the content while the page header keeps its own side padding (so the title never touches the viewport edge).

  • A leading EntityHeader becomes the page header. When a page’s content starts with an EntityHeader (the guest/record banner), the renderer hoists it into the canonical page header: its title becomes the page title with the badges beside it, its subtitle the page subtitle, and its facts + metric render as a row of label/value pairs under the title — the banner is not repeated in the body. This is the Redwood record-header anatomy without any extra annotation: compose the EntityHeader as the first component and the header comes out integrated. Applies to top-level pages only; embedded islands keep their banner inline.

  • Secondary header actions collapse into a ”…” menu. Toolbar buttons marked primary stay visible in the page header; when a page declares two or more secondary actions they collapse into an overflow menu (the Redwood header grammar), keeping the header to one primary action plus ”…”. A single secondary action stays inline.

  • The page type is inferred from the ModelView. Every page carries a coarse pageType on the wire — landing (Welcome, HeroSearch), collection (listings, search pages, CRUDs, to-do lists, calendars, collection detail), detail (overviews, foldout, master-detail), form (plain reflected forms), process (wizards), dashboard (archetype, or any ModelView with MetricCard fields). Archetypes declare theirs through the mapping; declare it explicitly with @PageTemplate(PageType.X) — or, on a YAML page, with a top-level pageType: landing key (YAML pages); declared, either wins over inference. The type anchors the width default (form/process/landing are always capped; collection/detail/dashboard defer to the content inference) and is stamped as data-page-type on the rendered page host as a stylesheet hook.

  • Peer navigation. A detail/overview page can implement PeerNavigationSupplier to add the previous/next-object arrows to the header (the Redwood “next/previous object” element): return a PeerNav(prevLabel, prevRoute, nextLabel, nextRoute) for the current record — a null route disables that side. Ported to .NET (IPeerNavigationSupplier) and Python (PeerNavigationSupplier). Demo: /peer-nav-demo.

    Peer navigation: the ‹ › arrows in the page header step to the previous/next record

  • Header overline and title placeholder. Two small text elements of the Redwood canonical header. @Overline("Requisitions") puts a quiet line above the title — a category, a parent context, a step marker. @TitlePlaceholder("New booking…") supplies what the header shows while the title is still empty (the create-mode affordance); it is a placeholder, not a default, so it disappears the moment a title exists. Both follow the house rule for page metadata: implement OverlineSupplier / TitlePlaceholderSupplier when the text depends on runtime state, and the supplier wins over the annotation. They travel on the wire as PageDto.overline / PageDto.titlePlaceholder.

  • Header timestamp. Mark a field with @Timestamp("Last updated") to show a “last updated” line in the header (the Redwood timestamp element); the field’s value renders as text and the field is dropped from the form body. Header key/value facts come from @KPI fields the same way. Ported to .NET ([Timestamp]) and Python (Timestamp()).

  • On Redwood these elements use the oj-sp page header’s own: the peer arrows are its previous/next item actions, the first header badge is its status badge (more badges follow the subtitle), and a timestamp whose value is a date (2026-10-12T10:42, label empty or “Last updated”) is its timestamp element, which writes its own translated “Last updated …”; any other timestamp text ends the subtitle line. The header has no overline or placeholder element, so the renderer draws the overline above the title and dims the placeholder title. Demo: demo-vb /header-extras (Java) and /yaml-header (the same page in YAML).

  • Backend parity. Everything on this page also works on the .NET and Python backends — see the parity matrix for the per-feature detail.

  • Composition beats templates. When no template fits, the same pieces compose freely: HeroSection, EntityHeader, TaskQueue, Card, property-list sections, zones and the fluent FormField are the vocabulary the archetypes themselves are written in.

  • Uniform content-page slot grammar. The templates with a main + contextual aside shape (ItemOverview — key-info panel + tabs; CollectionDetail — searchable list + detail pane) compose a single ResponsiveGrid template: named areas (gridTemplateAreas, e.g. "keyinfo tabs"), the content dropped into them as Slotted children, stickyAreas for a pinned region and stackBelow for the width under which the areas stack — instead of each reinventing a bespoke layout. The aside sits beside the main region on wide viewports and stacks under it when narrow. It is a wire component like FoldoutLayout, so you can also compose it directly in a custom view; ported to .NET and Python. (The older ContentLayout with main/aside/footer regions is deprecated: no archetype produces it any more.)

  • @Aside — content-page from a plain form. The minimal way to get the content-page grammar without composing anything: on an ordinary reflected form, mark one component-holder field @Aside (.NET [Aside], Python Aside()). That field is pulled out of the form body and placed in the aside area; the rest of the form becomes the main area of the same kind of grid template (position/width/sticky come from the annotation). You keep declaring data as usual and just point at the one supporting panel that belongs to the side.

Every header element on this page — overline, title placeholder, timestamp, badges, KPIs, peer navigation, the record switcher, banners and page FABs — is authorable on a YAML page’s type: Form too, with the same wire fields. See YAML pages › Page header.

The Redwood header’s selectObject / selectContext element, available on any page with a header — not only inside GeneralOverview. Implement RecordSwitcherSupplier (the sibling of PeerNavigationSupplier: peer navigation steps to the previous/next record, the switcher jumps to any of them):

@UI("/customer")
public class CustomerPage implements RecordSwitcherSupplier {
public String customer = "c1";
@Override
public RecordSwitcher switcher(HttpRequest rq) {
return RecordSwitcher.builder()
.options(customers.stream().map(c -> new Option(c.id(), c.name(), c.city())).toList())
.value(customer)
.type(SwitcherType.object) // or context: what the page is evaluated in
.label("Customer")
.searchable(true) // type-ahead over label + description
.build();
}
@Override
public Object switchTo(String value, HttpRequest rq) {
customer = value; // point the page at the new record…
return this; // …and re-render in place (or return a URI to navigate)
}
}

It travels as PageDto.switcher; a pick runs the page’s _switchRecord action with the value in _record, which calls switchTo. Vaadin draws a native select (a filtering combobox when searchable) beside the title; Redwood uses the oj-sp header’s own data switcher (with switcherSearch); React Native a pill opening a bottom sheet; IntelliJ a combo box with speed search.

The header switcher, searching

The Redwood announcement slot — what a screen-reader user should hear when nothing on screen takes focus — is a command any action can return: UICommand.announce("3 rows imported"), or UICommand.announceAssertive(text) for errors (it interrupts). Nothing is drawn; each renderer pushes the text through its live region (web), AccessibilityInfo (React Native) or the IDE’s announcer (IntelliJ). The CRUD edit drawer already announces a failed save.

Redwood switches a template’s built-in affordances through displayOptions, each on, off or disabled. Mateu’s equivalent is the shared Toggle enum (on · off · disabled = shown but inert — the state permissions need) grouped in one display record per archetype, returned by an overridable display():

ArchetypeRecordToggles
WizardWizardDisplaysaveDraft, saveAndClose, skip
Crud / AutoCrudCrudDisplaycreate, delete, saveAndNext, errorBanner
GeneralOverviewGeneralOverviewDisplayinfo, promoteInfoSlot

Toggles are consumed on the server while composing — a disabled affordance travels as a disabled button and is refused if forced, an off one does not travel — so no renderer needs to know about them. The existing gates (canCreate, @NotDeletable…) still decide whether an affordance exists; the display record decides how it shows. Only the archetypes with affordances worth switching have a record: inventing toggles for the others would be surface without behaviour.

The Redwood section template’s edit / add / view-more actions, on any @Section:

@Section(value = "Guests", addAction = "addGuest", editAction = "editGuests")
public String leadGuest;
@Section(value = "Payments", viewMoreAction = "allPayments")
public String lastPayment;

Each attribute names an action method of the form: Add and Edit render as small tertiary buttons on the section’s title row, View more as a link under its content. They are ordinary buttons composed on the server, so every renderer draws them.

Coverage of the header and cross-template pieces

Section titled “Coverage of the header and cross-template pieces”
Java.NETPythonVaadinRedwoodReact NativeIntelliJ
Record / context switcher✅✅✅✅✅✅✅
Announcements✅✅✅✅✅✅✅
Display records (Toggle)✅✅✅✅✅✅✅
Section affordances✅✅✅✅✅✅✅
Peer navigation, overline, title placeholder, timestamp✅✅✅✅✅✅✅