- Accordion
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- Checkbox
- Combobox
- Container
- CurrencyInput
- DistributionSlider
- Drawer
- Dropdown
- Grid
- Heading
- Image
- Input
- InputGroup
- Label
- Logo
- MapPin
- Modal
- NativeSelect
- NumberInput
- OtpInput
- PhoneInput
- Popover
- Progress
- PropertyCalendar
- RadioGroup
- RadioGroupCards
- ResponsiveModal
- ScrollArea
- SearchBar
- SearchBarFallback
- SearchInput
- Select
- Separator
- Spinner
- Switch
- Tabs
- Text
- Textarea
- Toast
- Toggle
- ToggleGroup
- Tooltip
Tabs
Organize content into switchable panels triggered by a tab bar
pnpm add @wandercom/design-system-web
The Tabs component provides both a high-level convenience API and low-level compound components for full composition control. Built on Base UI's Tabs primitive.
Use the Tabs component for simple tab bars without associated content panels:
import { Tabs } from '@wandercom/design-system-web/ui/tabs';
export function Example() {
const [selected, setSelected] = useState("all");
return (
<Tabs
items={[
{ label: "All reviews", value: "all" },
{ label: "Published", value: "published" },
{ label: "Drafts", value: "drafts" },
]}
value={selected}
onChange={setSelected}
/>
);
}For tab bars with associated content panels, use the individual primitives. Place <TabsIndicator /> inside <TabsList> to get the animated active-tab indicator:
import {
TabsRoot,
TabsList,
TabsTrigger,
TabsIndicator,
TabsContent,
} from '@wandercom/design-system-web/ui/tabs';
export function Example() {
return (
<TabsRoot defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Details</TabsTrigger>
<TabsTrigger value="tab2">Reviews</TabsTrigger>
<TabsIndicator />
</TabsList>
<TabsContent value="tab1">Property details here.</TabsContent>
<TabsContent value="tab2">Guest reviews here.</TabsContent>
</TabsRoot>
);
}Pill-shaped tab triggers with a filled background on the active tab. Supports sm and md sizes.
<TabsRoot defaultValue="tab1" variant="default" size="md">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsIndicator />
</TabsList>
</TabsRoot>Borderless tab triggers with an underline on the active tab. Size prop is ignored; height is fixed at 40px. Use the bordered prop on TabsList to add a full-width bottom border.
<TabsRoot defaultValue="tab1" variant="underline">
<TabsList bordered>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsIndicator />
</TabsList>
</TabsRoot>items
variant?
size?
bordered?
value?
defaultValue?
onChange?
className?
classNames?
variant?
size?
defaultValue?
value?
onValueChange?
orientation?
className?
bordered?
className?
value
disabled?
className?
value
className?
Animated indicator that slides between active tabs. Place it as a child of TabsList. Base UI positions it automatically via CSS custom properties (--active-tab-width, --active-tab-left).
For the default variant, it renders as a pill-shaped background behind the active tab. For the underline variant, it renders as a sliding bottom border.
The high-level Tabs component includes TabsIndicator automatically. When using compound components, add <TabsIndicator /> inside <TabsList> yourself.
className?
Built on Base UI's Tabs primitive, which follows the WAI-ARIA Tabs pattern.
Keyboard interaction:
ArrowLeft/ArrowRight- Navigate between tabs (horizontal orientation)ArrowUp/ArrowDown- Navigate between tabs (vertical orientation)Home- Move focus to the first tabEnd- Move focus to the last tabEnter/Space- Activate the focused tab
Only the active tab is in the tab sequence (tabIndex={0}). Inactive tabs use tabIndex={-1} so the user can tab past the entire tab list in one keystroke. Each tab is linked to its panel via aria-controls and aria-selected.
Reduced motion: The TabsIndicator slide animation respects prefers-reduced-motion: reduce. When the user's OS preference is set to reduce motion, the indicator transitions are disabled via motion-reduce:transition-none.