Knit UI
GitHub

Usage

Tabs is a compound component: a Tabs.List of Tabs.Tabs, and a Tabs.Panel for each one. The value on a tab and its panel are what pair them — there is no index to keep in sync.

import { Tabs } from "@knitui/components";

<Tabs defaultValue="general">
  <Tabs.List>
    <Tabs.Tab value="general">General</Tabs.Tab>
    <Tabs.Tab value="billing">Billing</Tabs.Tab>
  </Tabs.List>

  <Tabs.Panel value="general">Account settings</Tabs.Panel>
  <Tabs.Panel value="billing">Cards and invoices</Tabs.Panel>
</Tabs>;

Leave value off for uncontrolled tabs, or pass value + onChange to drive it yourself. orientation="vertical" moves the list to the side (placement picks which side), and inverted puts it after the panels.

By default inactive panels stay mounted and hidden (keepMounted), so state inside a panel survives switching away from it. Set keepMounted={false} when a panel is expensive — a map, a chart, a video — and should not exist until it's selected.

When to use something else

  • Panels that expand in place rather than replacing each other → Accordion
  • A small set of mutually exclusive values, not views → SegmentedControl
  • Navigating between routes → NavLink

Examples

Variants

All three visual variants side by side.

Loading example…

VariantsSourceStorybook

Sizes

Full token size scale.

Loading example…

SizesSourceStorybook

Shadows

The shadow elevation prop, inherited from Box, applied to the Tabs root.

Loading example…

ShadowsSourceStorybook

Themed

Accent theme comes from Tamagui's theme prop.

Loading example…

ThemedSourceStorybook

With Disabled Tab

A disabled tab cannot be activated and is visually dimmed.

Loading example…

With Disabled TabSourceStorybook

With Sections

Tabs with left/right icon sections for visual adornment.

Loading example…

With SectionsSourceStorybook

Vertical

Vertical orientation with the list on the left side (default placement).

Loading example…

VerticalSourceStorybook

Vertical Right

Vertical orientation with the tab list placed on the right side.

Loading example…

Vertical RightSourceStorybook

Inverted

Inverted layout — the tab list renders below the panel content.

Loading example…

InvertedSourceStorybook

Controlled

Controlled — the active tab is owned by the parent component.

Loading example…

ControlledSourceStorybook

Growing Tabs

grow makes tabs expand to fill the full width of the list.

Loading example…

Growing TabsSourceStorybook

Styles

Per-slot styles targets individual parts — here the tab, label and panel.

Loading example…

StylesSourceStorybook

Props

PropTypeDefaultDescription
activateTabWithKeyboardboolean | undefinedtrueWhen an arrow key focuses a tab, also activate it. `false` = manual activation (focus moves, selection follows on Enter/Space). Web-only.
allowTabDeactivationboolean | undefinedfalseAllow clicking an active tab to deselect it.
childrenReactNode—`Tabs.List` + `Tabs.Panel` children.
defaultValuestring | null | undefinedgeneralUncontrolled initial value.
disabled systemboolean | undefined——
idstring | undefined—Base id for a11y ids. Auto-generated if omitted.
invertedboolean | undefinedfalseRender the tab list after the panels.
keepMountedboolean | undefinedtrueKeep inactive panels mounted (hidden) in the DOM.
loopboolean | undefinedtrueWrap arrow-key roving focus at the ends.
onChange((value: string | null) => void) | undefined—Called when the active tab changes.
orientationTabsOrientation | undefined
horizontal · vertical
'horizontal'Layout axis of the tab list.
placementTabsPlacement | undefined
left · right
'left'Side the list sits on when orientation is vertical.
radius systemstring | number | undefined——
shadow system"xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined——
size system"xs" | "sm" | "md" | "lg" | "xl" | "xxs" | "xxl" | undefined
xxs · xs · sm · md · lg · xl · xxl
'md'Size of tab labels and spacing.
styles systemSlotStyles<TabsStyles> | undefined—Uniform per-slot style passthrough — sugar over the composable parts. Slots: `list` / `tab` / `label` / `section` / `panel`. Distributed through context so it reaches every nested `Tabs.Tab`/`Tabs.Panel`. Explicit inline props on a composed part always win.
theme systemThemeName | null | undefined—Applies a theme to this element
valuestring | null | undefined—Controlled active tab value.
variant systemTabsVariant | undefined
default · outline · pills
'default'Visual style of the tab indicator.

Style slots

Every part of Tabs can be styled through the styles prop. Explicit props on the component always win over slot styles.

SlotTargets
labelProps for the label text inside each tab (.Tab label).
listProps for the Tabs.List row (.List).
panelProps for each Tabs.Panel (.Panel) — its stylable frame surface; value/children are the part's own (identity + content).
sectionProps for the left/right section wrapper inside each tab (.Section).
tabProps for each Tabs.Tab trigger (.Tab) — its stylable frame surface. active/disabled/size/variant are state/context-driven and value/ children are the part's own, so the sugar targets the frame like siblings.
Accessibility & DOM props (2)
PropType
keyKey | null | undefined
refRef<TamaguiElement> | undefined

Plus 495 inherited style props from Box — the full Tamagui/React Native style surface, including token shorthands like p, mx, bg and c. See Tokens for the scales they accept, or the full list.

Accessibility

On the web Tabs implements the ARIA tabs pattern: the list is a tablist, each tab a tab owning its panel via aria-controls, and each panel a tabpanel labelled by its tab. Ids are generated for you, or derived from the id prop.

Focus is managed with a roving tabindex — only the active tab is in the tab order, so Tab moves past the whole list rather than through every tab in it, and the arrow keys move within it.

KeyAction
TabMove into the tab list, then out to the active panel
→ / ←Previous / next tab (horizontal orientation)
↓ / ↑Previous / next tab (vertical orientation)
Home / EndFirst / last enabled tab
Enter / SpaceActivate the focused tab

Two behaviours are worth choosing deliberately:

  • activateTabWithKeyboard (default true) selects a tab as soon as an arrow key focuses it — automatic activation, which ARIA recommends when switching is instant. Set it to false for manual activation (focus moves, selection waits for Enter or Space) when a panel is expensive to render, so arrowing across the list doesn't mount four panels on the way past.
  • loop (default true) wraps focus at the ends of the list. Turn it off when the list is long enough that wrapping is disorienting.

Disabled tabs are skipped by arrow navigation and are never focused.

Edit this page on GitHub