Panel switcher with pill, line, and segment variants. Prefer Content Switcher when you only switch mode without tab panels.
Also known as: tab bar, tab panels, tabbed panels, panel tabs
Install with the CLI, or copy the component source into your project.
Add tabs (npm)
1npx --yes -p @default-file/ui df-ui add tabs
The standard tab style uses a soft track with an animated active indicator.
Underline indicators for docs and content chrome. By default, the divider only spans the tab labels. The second example stretches the divider across the full width.
Stretch the underline across the full width. Align to the start for tabs on the left, or to the end for tabs on the right.
Equal width mode switcher with a sliding active chip. Used in Color Picker for Hex, RGB, HSL, and HSB.
Stack triggers in a column. Works with every variant: pill, line, and segment. Arrow keys move Up and Down, and the vertical pill track softens its default radius so the column reads as a rounded panel rather than a capsule.
In a vertical layout, the list sits to the left and the active panel fills the space beside it. This is a common settings pattern.
Leading icons, trailing count badges, and disabled triggers work the same in vertical orientation, on both pill and line.
Choose which edge carries the divider and active indicator. In a vertical layout, use left or right (right by default). In a horizontal layout, use top or bottom (bottom by default). Prefer left when the list sits to the right of its content so the line faces the panel.
Leading icons, trailing count badges, and disabled triggers on pill and segment.
Control the space inside a trigger between icon, label, and badge, and the space between triggers. Both use design-scale units from the shared spacing scale (none or 0 through 200).
Small, default, and large tabs.
Corner radius for the track and sliding chip. Pill defaults to full; segment defaults to lg.
Panel switcher with pill, line, and segment variants in horizontal or vertical orientation. Label ink is optically centered to cap height with text-box when available, and a 1cap line box otherwise. Compose Tabs, TabsList, TabsTrigger, and TabsContent.
| Prop | Type | Default | Notes |
|---|---|---|---|
| variant | "pill" | "line" | "segment" | "pill" | Visual style. pill = soft rounded track; line = underline; segment = equal width mode switcher with a sliding chip (e.g. color input modes). |
| size | "sm" | "default" | "lg" | "default" | Trigger height and type size. |
| orientation | "horizontal" | "vertical" | "horizontal" | Layout axis. horizontal puts triggers in a row with the panel below; vertical stacks triggers in a column with the panel alongside. Arrow key navigation switches to Up/Down. |
| lineSide | "top" | "bottom" | "left" | "right" | "bottom" (horizontal) / "right" (vertical) | For the line variant only: which edge holds the divider and active indicator. Horizontal layouts accept top or bottom; vertical layouts accept left or right. Prefer left when the list sits to the right of its content so the line faces the panel. |
| lineAlign | "start" | "end" | - | For the line variant only. Leave unset so the list sizes to its triggers. Choose start or end to stretch the divider across the available width and group the tabs on that side. In a horizontal layout, start places tabs on the left and end places them on the right. In a vertical layout, start places tabs at the top and end places them at the bottom. |
| radius | "none" | "sm" | "md" | "lg" | "xl" | "2xl" | "full" | - | Corner radius for the track, sliding chip, and triggers. Defaults: pill full (2xl when vertical), segment lg. Line ignores radius. |
| cornerShape | "round" | "smooth" | - | Corner curve for pill and segment chrome. Omit to inherit --df-corner-shape from the theme. |
| gap | number | "none" | 1.5 | Gap inside each trigger between leading icon, label, and trailing badge. Design-scale units from the shared spacing scale (none/0, even integers through 200, half-steps like 0.5 and 1.5). One unit = 0.25rem via --spacing-unit. |
| spacing | number | "none" | 0 (pill/line) / 0.5 (segment) | Gap between triggers in the list. Same design-scale units as gap. Defaults keep pill and line flush and segment slightly open. |
| value | string | - | Controlled active tab value. |
| defaultValue | string | - | Uncontrolled initial tab value. |
| onValueChange | (value: string) => void | - | Called when the active tab changes. |
| className | string | - | Additional styles on the root. |
Container for triggers. Inherits variant, size, and radius from Tabs. Hosts the animated active indicator.
| Prop | Type | Default | Notes |
|---|---|---|---|
| className | string | - | Additional styles on the list. |
| children | ReactNode | - | TabsTrigger children. |
| Prop | Type | Default | Notes |
|---|---|---|---|
| value | string | - | Tab value this trigger activates (required). |
| leading | ReactNode | - | Icon or node before the label. |
| trailing | ReactNode | - | Node after the label. Pass a Badge with a count for notifications. |
| disabled | boolean | - | Non-interactive trigger. Skipped by pointer and selection. |
| children | ReactNode | - | Trigger label. |
Selected panels are focusable (tabIndex 0). Inactive panels omit the tab stop unless forceMount keeps them mounted with tabIndex unset.
| Prop | Type | Default | Notes |
|---|---|---|---|
| value | string | - | Shows when this value is active (required). |
| forceMount | boolean | false | Keep content mounted when inactive. |
| children | ReactNode | - | Panel content. |
| className | string | - | Additional styles on the panel. |
Equal width segmented control for compact mode switching. The active chip slides between options with transform animation. Animated indicator (built-in): TabsList renders a sliding background chip behind the active trigger. Honors prefers-reduced-motion.
Theme defaults live on :root. Override these globally or on a local host.
| Token | Type | Default | Notes |
|---|---|---|---|
| --df-tabs-indicator-thickness | CSS length | var(--border-width-thick) | Line variant indicator thickness. |
| --df-shadow-tabs-indicator | CSS shadow | 0 1px 2px color-mix(in oklch, var(--foreground) var(--df-shadow-intensity-lg), transparent) | Shadow under the sliding indicator. |
| --df-tabs-gap | CSS length | - | Instance variable set by the gap prop. Sizes the gap between leading icon, label, and trailing badge inside a trigger. |
| --df-tabs-spacing | CSS length | - | Instance variable set by the spacing prop. Sizes the gap between triggers in the list. |
| --df-tabs-panel-gap | CSS length | - | Instance variable set between the list and its panel. |
| --muted | CSS color | var(--df-neutral-50) | Pill variant track fill. |
| --background | CSS color | var(--df-neutral-0) | Active trigger and sliding chip fill. |
| --foreground | CSS color | var(--df-neutral-900) | Active trigger label. |
| --border | CSS color | var(--df-neutral-100) | Line variant divider. |
| --df-corner-shape | CSS corner-shape | var(--df-corner-shape-round) | Theme corner curve for all kit chrome. round = circular arcs; smooth = continuous squircle. Set with df-ui init --corner-shape, or override on :root. Default round. |
| --df-label-line-height | CSS line-height | var(--df-leading-none) | Line box for label flex centering. Defaults to --df-leading-none. When text-box is unavailable and the cap unit is supported, the kit sets this to 1cap so flex centering matches cap-height ink. |