Skip to content

Tabs

Organize content into separate views where only one view is visible at a time. Aurora's Tabs integrates with the DS_URL_STATE adapter so the active tab is persisted in the URL by default.

html
<Tabs default-tab="overview">
  <Tab name="overview" title="Overview">…</Tab>
  <Tab name="metrics" title="Metrics">…</Tab>
  <Tab name="history" title="History">…</Tab>
</Tabs>

By default each Tab renders inside a styled card panel (white background, rounded corners, padding) that connects visually with the tab button. Use the headless prop on a Tab to disable this card chrome and take full control of the panel's appearance.

About the docs examples

Examples on this page use border to make the panel chrome visible against the white docs background. In an app with a tinted page background, the white panel reads on its own and the border isn't needed.

Two variants, two jobs

Picking the right variant matters more than the styling — it communicates hierarchy.

VariantLives atMental modelExample
primaryPage level"Where am I globally?"Energy / GHG / Water / Waste
secondaryInside a container (card, panel, modal)"How do I want to view this?"YoY / MoM / YTD · Chart / Table

Primary tabs swap large portions of the page — feel like distinct states. Secondary tabs change how the same dataset is represented; the content stays the same, only the view changes.

Don't mix levels

Never stack two primary tab bars at the same level of a layout. If you need a sub-categorization, that's a secondary tab inside a panel — not another primary bar.

When to reach for tabs

  • Switching between primary page sections that feel like distinct states (Energy / GHG / Water / Waste)
  • Changing how a dataset is represented within a container (chart vs table, YoY vs MoM)
  • Content fits neatly into 2–6 named categories with short labels

When not to use tabs

  • More than ~6 categories — use a dropdown or side nav instead
  • Navigating between major pages or routes — that's the top nav or sidebar's job, not tabs
  • Triggering actions — use a button or button group
  • Sorting or filtering data — use a dropdown or SegmentedControl

Secondary variant

Sits inside a card, panel, or modal. Use to change the representation of a dataset — switching between chart and table view, time-window granularities, comparison modes.

html
<Tabs default-tab="overview" variant="secondary">
  <Tab name="overview" title="Overview">…</Tab>
  <Tab name="settings" title="Settings">…</Tab>
</Tabs>

With leading icon

html
<Tab name="overview" title="Overview" leading-icon="home">…</Tab>
<Tab name="settings" title="Settings" leading-icon="settings">…</Tab>

State indicators

Combine danger, warning, and missing flags to surface validation state on each tab.

html
<Tab name="ok" title="Complete">…</Tab>
<Tab name="warn" title="Has warnings" warning>…</Tab>
<Tab name="err" title="Has errors" danger>…</Tab>
<Tab name="missing" title="Missing data" missing>…</Tab>

Disabled tab

html
<Tab name="locked" title="Locked" disabled>…</Tab>

Tabs — Props

PropTypeDefaultDescription
defaultTabstringname of the tab to open initially.
variant'primary' | 'secondary''primary'Visual style.
paramKeystring'tab'URL query param the active tab is stored under.
disableRouterUpdatebooleanfalseDon't write the active tab to the URL.
borderbooleanfalseRender a border around the active tab's panel. Useful when the page background doesn't already make a white panel visible.
mobileTransparentbooleanfalseUse transparent background on small screens.

Tab — Props

PropTypeDefaultDescription
namestringrequiredStable identifier matched against the URL param and defaultTab.
titlestringrequiredTab label text.
leadingIconIconNameIcon shown before the title.
leadingIconClassstringTailwind classes applied to the leading icon.
trailingIcons{ icon: IconName; class?: string }[]Icons shown after the title (e.g. count badges, status indicators).
dangerbooleanfalseApply error-state styling to the tab.
warningbooleanfalseApply warning-state styling.
missingbooleanfalseApply missing-state styling.
disabledbooleanfalseDisable the tab; cannot be selected.
headlessbooleanfalseRender the tab content without the default panel chrome.
renderStrategy'lazy' | 'on-select' | 'pre-render''lazy'When the tab's content is mounted.

Events

EventDescription
changeEmitted when the active tab changes. Receives (name: string).

Adapter contract

Tabs reads and writes the active tab via DS_URL_STATE. Aurora's default URL-state adapter is a no-op, so out of the box the tab state lives in component state only. To enable URL persistence, provide a real adapter at the app root via provideUrlState(app).

Exposed methods

Tabs exposes a select(tabName) method via defineExpose — call tabs.value?.select('history') from a parent.

html
<Tabs ref="tabs" default-tab="overview">…</Tabs>