Skip to main content

fieldgroup — a titled set of related fields

UiFieldGroup wraps a handful of nodes in a native <fieldset><legend>…</legend>, so the group heading is announced together with every field inside it. That is the whole point: it buys real grouping semantics for less ceremony than a styled stack with a heading node.

It holds any nodes, not just fields — a nested stack for columns works fine. Drop it into a form's content list (groups are not valid in fields, which is typed to UiField).

Live — two groups, one of them containing a two-column stack. Press "Save": the toast lists the collected payload keys, showing that the grouping has no effect on the submitted object.

Fields

FieldTypeMeaning
idStringNode id and the <fieldset>'s DOM id. Omitted from the DOM when absent.
titleStringThe <legend>. Omit it and no legend is rendered.
hintStringSmall helper text under the legend, above the content.
contentList<UiNode>The grouped nodes — fields, or any layout node. Empty by default.
cssClassStringExtra CSS class on the <fieldset>.

Building one

UiForm.of("customer-form", "Customer")
.content(UiFieldGroup.of("contact", "Contact")
.hint("How we reach this customer.")
.field(UiField.text("email", "E-mail", null).asRequired().asEditable())
.field(UiField.text("phone", "Phone", null).asEditable()))
.content(UiFieldGroup.of("address", "Shipping address")
// any node works, not just fields
.content(UiStack.of(
UiField.text("zip", "ZIP", "10115").asEditable(),
UiField.text("city", "City", "Berlin").asEditable())
.direction(UiStack.Direction.HORIZONTAL).gap(12))
.field(UiField.select("country", "Country", "de", List.of(
UiField.Option.of("de", "Germany"),
UiField.Option.of("at", "Austria"))).asEditable()))
.action(UiAction.primary("save", "Save")
.onClick(UiTrigger.api("POST", "/customers", "customer-form")));

Notes

Transparent to submission. A group changes the DOM structure, never the payload. Fields keep their own name, and the client collects every named control in the surrounding <form> — so {"email": …, "zip": …, "city": …} comes out flat, whichever group each field sat in.

Use content, not fields, on the form. UiForm.fields is a List<UiField> and cannot hold a group. UiForm.content(node) takes any node and is rendered after the flat fields, so a form can mix a short flat list with grouped sections below.

A group is not a section. fieldgroup is a labelled box that is always visible; section is what you want for collapsible or tabbed bodies. They nest happily — a section entry holding a group is a common shape for long settings pages.

Nothing is patch-special about groups. Patch the field ids to update individual inputs; replace the group id only when the whole set changes shape (different fields, different legend).

See also

  • form — where groups live.
  • field — the inputs inside.
  • stack — plain layout without the <fieldset> semantics.
  • Forms — layout and validation patterns end to end.
  • Triggers & actions — submitting the collected payload.