Tabs

A set of layered sections of content that display one panel at a time.

1<Flex
2 direction="row"
3 gap={9}
4 style={{ width: "100%", fontSize: "12px", padding: "0 16px" }}
5>
6 <Tabs defaultValue="tab-one">
7 <Tabs.List>
8 <Tabs.Tab value="tab-one">Hosting</Tabs.Tab>
9 <Tabs.Tab value="tab-two">Billing</Tabs.Tab>
10 <Tabs.Tab value="tab-three">SEO</Tabs.Tab>
11 </Tabs.List>
12 <Tabs.Content value="tab-one">
13 <Text>Hosting configuration content</Text>
14 </Tabs.Content>
15 <Tabs.Content value="tab-two">

Anatomy

Import and assemble the component:

1import { Tabs } from "@raystack/apsara";
2
3<Tabs>
4 <Tabs.List>
5 <Tabs.Tab />
6 </Tabs.List>
7 <Tabs.Content />
8</Tabs>

Usage

Tabs are a list of triggers and the panels they show. Appearance is set on the root.

Basic usage

A tab list with panels. Set defaultValue to the tab that should open first.

1<div style={{ width: "400px" }}>
2 <Tabs defaultValue="tab1">
3 <Tabs.List>
4 <Tabs.Tab value="tab1">Account</Tabs.Tab>
5 <Tabs.Tab value="tab2">Password</Tabs.Tab>
6 <Tabs.Tab value="tab3">Settings</Tabs.Tab>
7 </Tabs.List>
8 <Tabs.Content value="tab1">Account settings</Tabs.Content>
9 <Tabs.Content value="tab2">Password settings</Tabs.Content>
10 <Tabs.Content value="tab3">Other settings</Tabs.Content>
11 </Tabs>
12</div>

Variants

Three treatments. segmented groups the tabs in a track, standalone gives them an underline, and plain strips the chrome for dense layouts.

1<div style={{ width: "400px" }}>
2 <Tabs defaultValue="tab1">
3 <Tabs.List>
4 <Tabs.Tab value="tab1">Account</Tabs.Tab>
5 <Tabs.Tab value="tab2" disabled>
6 Password
7 </Tabs.Tab>
8 <Tabs.Tab value="tab3">Settings</Tabs.Tab>
9 </Tabs.List>
10 <Tabs.Content value="tab1">Account settings</Tabs.Content>
11 <Tabs.Content value="tab2">Password settings</Tabs.Content>
12 <Tabs.Content value="tab3">Other settings</Tabs.Content>
13 </Tabs>
14</div>

Sizes

Three sizes. medium is the default; use small in toolbars and panels.

1<div style={{ width: "400px" }}>
2 <Tabs defaultValue="tab1" size="small">
3 <Tabs.List>
4 <Tabs.Tab value="tab1">Account</Tabs.Tab>
5 <Tabs.Tab value="tab2" disabled>
6 Password
7 </Tabs.Tab>
8 <Tabs.Tab value="tab3">Settings</Tabs.Tab>
9 </Tabs.List>
10 <Tabs.Content value="tab1">Account settings</Tabs.Content>
11 <Tabs.Content value="tab2">Password settings</Tabs.Content>
12 <Tabs.Content value="tab3">Other settings</Tabs.Content>
13 </Tabs>
14</div>

With leading icons

Pass leadingIcon on a trigger to place an icon before its label.

1<div style={{ width: "400px" }}>
2 <Tabs defaultValue="tab1">
3 <Tabs.List>
4 <Tabs.Tab value="tab1">Home</Tabs.Tab>
5 <Tabs.Tab value="tab2" leadingIcon={<Info />}>
6 Info
7 </Tabs.Tab>
8 </Tabs.List>
9 <Tabs.Content value="tab1">Home content</Tabs.Content>
10 <Tabs.Content value="tab2">Info content</Tabs.Content>
11 </Tabs>
12</div>

Disabled tab

Set disabled on a trigger to keep a tab visible but unreachable.

1<div style={{ width: "400px" }}>
2 <Tabs defaultValue="tab1">
3 <Tabs.List>
4 <Tabs.Tab value="tab1">Active</Tabs.Tab>
5 <Tabs.Tab value="tab2" disabled>
6 Disabled
7 </Tabs.Tab>
8 </Tabs.List>
9 <Tabs.Content value="tab1">Active tab content</Tabs.Content>
10 <Tabs.Content value="tab2">Disabled tab content</Tabs.Content>
11 </Tabs>
12</div>

Controlled

Pass value with onValueChange to own the active tab — needed when something outside the tab list has to switch panels, or when the tab is part of the URL.

1(function ControlledTabs() {
2 const [tab, setTab] = React.useState("overview");
3
4 return (
5 <Flex direction="column" gap={5} style={{ width: "100%" }}>
6 <Tabs value={tab} onValueChange={setTab}>
7 <Tabs.List>
8 <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
9 <Tabs.Trigger value="usage">Usage</Tabs.Trigger>
10 </Tabs.List>
11 <Tabs.Content value="overview">Overview panel</Tabs.Content>
12 <Tabs.Content value="usage">Usage panel</Tabs.Content>
13 </Tabs>
14 <Button size="small" onClick={() => setTab("usage")}>
15 Go to Usage

API Reference

A root holding the tab list, and one content panel per tab.

Root

Groups all parts of the tabs component.

Prop

Type

List

Renders the tab navigation bar.

Prop

Type

Tab

Renders an individual tab button.

Prop

Type

Content

Renders the content panel for a tab.

Prop

Type

Slots

Every rendered part carries a stable data-slot attribute for styling and testing:

SlotElement
tabsThe root element
tabs-listTabs.List container
tabs-indicatorThe animated active-tab indicator
tabs-tabTabs.Tab button
tabs-tab-iconA tab's leading icon (when leadingIcon is set)
tabs-contentTabs.Content panel

Accessibility

  • Follows the WAI-ARIA Tabs pattern
  • Supports keyboard navigation with arrow keys between tabs
  • Uses role="tablist", role="tab", and role="tabpanel"
  • Active tab is indicated with aria-selected
  • Leading icon is wrapped with aria-hidden so it is not announced (treated as decorative)
  • Respects motion preferences: tab indicator motion is enabled only when prefers-reduced-motion: no-preference