Skip to content

Layouts

Layouts are positional containers — row(), col(), and grid() — that arrange other views spatially. They carry no data of their own and expose no endpoints; only the leaf specs they contain (table, form, stat, etc.) handle data fetching.

INFO

Layouts carry no data of their own and have no endpoints. They are positional containers only — the leaf specs they contain (table, form, stat, etc.) handle their own data fetching via their own endpoint config.

Flex layout

row() and col() create flex containers. row() lays children out horizontally; col() stacks them vertically.

col() — stacked layout

The most common real-world pattern: a filter bar on top, a data table below.

Preview
Filter form
Table
Spec
typescript
import { col, pageSpec } from '@retrofit-ui/builder-zod';

pageSpec()
  .add(
    col('16px')
      .filterForm(filterSpec)
      .table(tableSpec)
      .build()
  )
  .build();

row() — side-by-side layout

Preview
Left panel
Right panel
Spec
typescript
import { row, pageSpec } from '@retrofit-ui/builder-zod';

pageSpec()
  .add(
    row('16px')
      .form(formSpec)
      .table(tableSpec)
      .build()
  )
  .build();

Flex props

PropTypeDefaultNotes
direction'row' | 'column''row' / 'column'Set by row() / col()
gapstring(none)Any CSS gap value, e.g. '16px'
wrapbooleanfalseWhether flex children wrap to the next line
align'start' | 'center' | 'end' | 'stretch'(none)CSS align-items
justify'start' | 'center' | 'end' | 'space-between' | 'space-around'(none)CSS justify-content

wrap, align, and justify exist on the underlying ViewSpec type but are not currently exposed as arguments on row() / col() — only gap is available as a builder shorthand.

Grid layout

grid(columns, gap?) creates a CSS grid with n equal-width columns (repeat(n, 1fr)).

Preview
$48,290
Revenue
1,204
Active Users
Spec
typescript
import { grid, pageSpec } from '@retrofit-ui/builder-zod';

pageSpec()
  .add(
    grid(2, '16px')
      .add(statSpec1)
      .add(statSpec2)
      .build()
  )
  .build();

Grid props

PropTypeDefaultNotes
columnsnumberNumber of equal-width columns (repeat(n, 1fr))
columnTemplatestringRaw grid-template-columns value; overrides columns
gapstring(none)Any CSS gap value

columns and columnTemplate are mutually exclusive — when both are set, columnTemplate takes precedence as it is used directly as the grid-template-columns CSS value.

Nesting

Flex and grid containers can be composed to arbitrary depth. A common pattern is a col() outer container with a row() inner container for a side-by-side pair:

Preview
Filter form
Form
Table
Spec
typescript
import { col, row, pageSpec } from '@retrofit-ui/builder-zod';

pageSpec()
  .add(
    col('24px')
      .filterForm(filterSpec)      // stacked above
      .add(
        row('16px')
          .form(formSpec)          // side-by-side
          .table(tableSpec)
          .build()
      )
      .build()
  )
  .build();

Quick reference

BuilderKindDescription
row(gap?)flexHorizontal flex container (direction: 'row')
col(gap?)flexVertical flex container (direction: 'column')
grid(columns, gap?)gridCSS grid with n equal columns

All three return a LayoutContainerBuilder with these chainable methods:

  • .add(spec) — append any ViewSpec (leaf or nested layout)
  • .form(spec, title?) — shorthand for .add({ kind: 'form', spec, title })
  • .table(spec) — shorthand for .add({ kind: 'table', spec })
  • .filterForm(spec) — shorthand for .add({ kind: 'filter-form', spec })
  • .markdown(spec) — shorthand for .add({ kind: 'markdown', spec })

Call .build() to produce the ViewSpec object to pass to pageSpec().add(...).