Skip to content

Tabs

Tabs::make()->schema([...]) renders a tab strip of Tab::make('value', 'Label')->schema([...]) children. The active tab is kept in the URL query string, and a tab’s content renders lazily on first open.

Tabs::make()->defaultValue('details')->schema([
Tab::make('details', 'Details')->schema([
Text::make('Team details go here.'),
]),
Tab::make('history', 'History')->schema([
Text::make('Recent activity for the team.'),
]),
]);
  • ->defaultValue('details') picks the tab shown when the URL names none. Otherwise the first tab wins.
  • ->queryKey('tab') sets the query-string parameter that tracks the active tab (defaults to tabs), so tabs survive reloads and are shareable by URL.
  • ->orientation(Orientation::Vertical) moves the strip to the side of the panels. Vertical tabs render as a fixed-width rail on the page background — no pill panel — with the active item marked by a primary accent bar and a subtle background.
  • ->alignment(TabsAlignment::…) controls how the strip sits. Horizontal tabs default to Stretch (tabs share the full width); Start, Center, and End shrink the strip to its content and pin it left, center, or right. Vertical tabs only read End — it moves the strip to the right of the panels; every other value keeps it on the left.
  • ->sticky() keeps a vertical rail in view while a long panel scrolls. The rail sits a gap below whatever sticky chrome the page publishes above it — a sticky Topbar, a sticky Stack header — so it never slides underneath. Horizontal strips ignore the option.

See the enums reference for the full Orientation and TabsAlignment cases.

Below the md breakpoint, tabs collapse into a native select: vertical tabs always (a side rail does not fit a phone), horizontal tabs once they have more than three entries. Selecting an option switches panels exactly like clicking the tab, including the password-confirmation redirect for gated tabs. A horizontal strip with three or fewer tabs is kept as-is.