ObjectStackObjectStack

Field grouping & order

The data model is a flat field set, but forms need sections. Where grouping actually lives — semantic field.group vs form sections vs a table's row grouping — and why those three "groups" are different things.

Field grouping & order

Scenario

From the object-definition angle, fields are flat. In a table they're flat columns too. But a form needs them grouped into sections. Where does the grouping live — on the field, or on the form? And do I have to re-group on every form?

Grouping is a property of a presentation, not of the field set. The model stays flat; a form imposes grouping; a table doesn't. The same fields, three lenses:

LensField shapeWhy
Object definitionflat field setthe model describes what data exists, not how to arrange it
Table / gridflat columnsa grid is a record × field matrix; the field axis is naturally flat
Form / record pagegrouped into sectionsa form shows one record to a human, who needs visual chunking

So "flat vs grouped" is not a contradiction — it's one flat set seen through different lenses.

You declare grouping once; surfaces inherit it

There are two grouping concepts. Keep them distinct:

1. Semantic grouping — field.group (on the object). A field's logical home ("billing", "contact_info", "system"). It travels with the model. The Studio field editor folds fields by it, and auto-generated forms use it as the default sectioning — so you are not starting from zero.

fields: {
  name:  Field.text({ label: 'Full name', group: 'contact' }),
  email: Field.email({ label: 'Email',    group: 'contact' }),
  stage: Field.select({ label: 'Stage',   group: 'status', options: [/* … */] }),
}

2. Layout grouping — form sections (on a view). A specific form's explicit arrangement: which fields, which section, how many columns, collapsible. It can inherit field.group as the default or override it per form.

form: {
  type: 'simple',
  sections: [
    { name: 'contact', label: 'Contact', columns: 2, fields: ['name', 'email', 'phone'] },
    { name: 'status',  label: 'Status',  columns: 2, fields: ['stage'] },
  ],
}

Order is the order you author in

There is no field.order. The order you declare fields in the object is the default display order everywhere — and form sections (and the fields within them) are themselves an ordered list, so group order and intra-group order are captured at authoring time. Both the create and edit projections inherit that order, which keeps "quick-create subset" and "full form" visually continuous (see Create form ≠ edit form).

The trap: a table's "group" is a different thing

A grid view also has grouping / groupByField — but that groups records (rows) by a field's value (all stage = qualified rows together), not fields (columns) into sections. The word "group" means two unrelated axes:

Surface"group" meansAxis
Formfields → sectionscolumns (visual chunking)
Gridrecords → buckets by valuerows (data pivot)

Conflating these two is the single most common source of exactly this confusion. They share a word and nothing else.

Why

Keeping grouping off the field (beyond an optional semantic hint) is what lets one model project to many surfaces: a create form, a full edit form, a mobile form, a public intake form, audience-specific layouts, a flat table, and an API — each arranging the same fields differently. Weld a single layout onto the field and you get exactly one arrangement everywhere, which every surface must then share. The semantic field.group is the deliberate middle ground: a default that travels, without dictating layout.

Runnable example

Anti-patterns

  • Adding structural nesting to the data model to satisfy a form. The model is flat; let the form group.
  • Re-typing the grouping in every form. Declare field.group once; forms inherit and only override on real divergence.
  • Assuming a grid "group" will section your form fields. It buckets rows by value — a different axis entirely.

See also

On this page