Skip to content

shadcn.io is an independent project built on shadcn/ui

Components

Free

Tabs for React Aria

Peer views of one subject, with only one panel visible at a time. For React with React Aria, hidden panels unmount unless you keep them mounted.

Tabs show one panel from a short list of peer views. Four tabs is already plenty.

Use them when people switch views of the same thing, rather than reading down the page. If more than one section should stay open, use an Accordion. In React, React Aria unmounts hidden panels, so a form there loses what was typed unless you keep it mounted.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Tabs anatomy: root, list, indicator, trigger, content
Tabs
├── TabsList
│   └── TabsIndicator
├── TabsTrigger
└── TabsContent

Usage

import {
  Card, CardContent, CardDescription, CardHeader, CardTitle,
} from "@/components/ui/card";
import {
  Tabs, TabsContent, TabsList, TabsTrigger,
} from "@/components/ui/tabs";
const TabsDemo = () => (
  <Tabs className="w-full max-w-sm" defaultSelectedKey="overview">
    <TabsList>
      <TabsTrigger id="overview">Overview</TabsTrigger>
      <TabsTrigger id="analytics">Analytics</TabsTrigger>
      <TabsTrigger id="reports">Reports</TabsTrigger>
      <TabsTrigger id="settings">Settings</TabsTrigger>
    </TabsList>
    <TabsContent id="overview">
      <Card>
        <CardHeader>
          <CardTitle>Overview</CardTitle>
          <CardDescription>
            View your key metrics and recent project activity. Track progress
            across all your active projects.
          </CardDescription>
        </CardHeader>
        <CardContent className="text-muted-foreground text-sm">
          You have 12 active projects and 3 pending tasks.
        </CardContent>
      </Card>
    </TabsContent>
    <TabsContent id="analytics">
      <Card>
        <CardHeader>
          <CardTitle>Analytics</CardTitle>
          <CardDescription>
            Track performance and user engagement metrics. Monitor trends and
            identify growth opportunities.
          </CardDescription>
        </CardHeader>
        <CardContent className="text-muted-foreground text-sm">
          Page views are up 25% compared to last month.
        </CardContent>
      </Card>
    </TabsContent>
    <TabsContent id="reports">
      <Card>
        <CardHeader>
          <CardTitle>Reports</CardTitle>
          <CardDescription>
            Generate and download your detailed reports. Export data in multiple
            formats for analysis.
          </CardDescription>
        </CardHeader>
        <CardContent className="text-muted-foreground text-sm">
          You have 5 reports ready and available to export.
        </CardContent>
      </Card>
    </TabsContent>
    <TabsContent id="settings">
      <Card>
        <CardHeader>
          <CardTitle>Settings</CardTitle>
          <CardDescription>
            Manage your account preferences and options. Customize your
            experience to fit your needs.
          </CardDescription>
        </CardHeader>
        <CardContent className="text-muted-foreground text-sm">
          Configure notifications, security, and themes.
        </CardContent>
      </Card>
    </TabsContent>
  </Tabs>
);

Accessibility

Tab
When focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content.
Arrow Down
Moves focus to the next trigger in vertical orientation and activates its associated content.
Arrow Right
Moves focus to the next trigger in horizontal orientation and activates its associated content.
Arrow Up
Moves focus to the previous trigger in vertical orientation and activates its associated content.
Arrow Left
Moves focus to the previous trigger in horizontal orientation and activates its associated content.
Home
Moves focus to the first trigger and activates its associated content.
End
Moves focus to the last trigger and activates its associated content.
Enter
In manual mode, when a trigger is focused, moves focus to its associated content.
Space
In manual mode, when a trigger is focused, moves focus to its associated content.

Demos

Line

The list drops the filled track and underlines the active trigger. Overview, Analytics, and Reports are the list, with no panels under them. Use the underline when the tabs sit on a page that already has cards, and another boxed list would stack containers. Keep the default list when the control is small and needs a visible boundary of its own.

Preview width

Vertical

orientation is vertical, so Account, Password, and Notifications stack, and the arrow keys that move between them are up and down. Only the list is on the page, and the panel belongs beside it. Use a vertical list when the labels are long enough to wrap in a horizontal row, such as a settings column. Two short words can stay horizontal.

Preview width

Disabled

Home can be selected. The settings trigger sets disabled, so it is skipped and cannot become the active tab. Use a disabled trigger for a section this account cannot open yet, a missing permission or an unfinished area, while the label stays as a promise. If it will never apply, drop the trigger rather than showing a control that goes nowhere.

Preview width

Icons

Preview and Code each lead with an icon, and the icons are aria-hidden because the words are the names. Use icons when the pair is a familiar mode switch, a preview against its source, and the picture helps at a glance. Do not ship icon-only triggers. The text is what gets announced, and a mystery glyph is not a tab label.

Preview width

Rtl

Arabic labels and card copy, with dir set to rtl. The list follows the reading order, and the panel text aligns to the start. The trigger values stay overview, analytics, reports, and settings, and only the visible words change. Use this for a localized product, not as a visual flip on an English page.

Preview width

FAQ

How do I control the selected tab in React?

Keep the selected tab's value in state and pass it back to Tabs; each trigger and panel is matched by its value. Use the default prop when you only need the first tab. With React Aria, pass selectedKey and onSelectionChange; each TabsTrigger and TabsContent is matched by its id.

How do I keep inactive Tabs panels mounted?

Inactive panels are unmounted by default, so their state resets when you switch back. Keep them mounted to preserve form input or scroll position. With React Aria, set shouldForceMount on TabsContent.

How do I make underline Tabs with React Aria?

Set variant="line" on TabsList, as the Line demo does: the list loses its background and the active tab gets an underline.

How do I show Tabs vertically?

Set orientation="vertical" on Tabs: the list stacks the triggers on the side and the Up and Down arrow keys move between them, as in the Vertical demo.

Are the Tabs accessible with the keyboard?

Yes. Tab moves focus into the list, the arrow keys move between triggers, and Home and End jump to the first and last; disabled triggers are skipped. The next Tab moves on to the open panel, which shows a focus ring. Focusing a tab selects it; set keyboardActivation="manual" to select only with Enter or Space.

Should I use Tabs or an Accordion?

Use Tabs when people switch between a few views of the same area, one at a time. Use an Accordion when sections stack vertically and several may be read in order, or on narrow screens where tab labels would not fit.

How do I install Tabs in React?

Run the command in the Installation section above: it adds Tabs and its parts to your ui folder. The command uses the shadcn CLI.

API Reference

Tabs

data-slot="tabs"

PropType
children?ChildrenOrFunction<T>

The children of the component. A function may be provided to alter the children based on component state.

Type
ChildrenOrFunction<T>
defaultSelectedKey?Key

The initial selected keys in the collection (uncontrolled).

isDisabled?boolean

Whether the TabList is disabled. Shows that a selection exists, but is not available in that circumstance.

keyboardActivation?"automatic" | "manual"

Whether tabs are activated automatically on focus or manually.

Type
"automatic" | "manual"
Default
"automatic"
onSelectionChange?(key: Key) → void

Handler that is called when the selection changes.

Parameters

key – Key

Returns
void
orientation?Orientation

The orientation of the tabs.

Default
"horizontal"
selectedKey?Key

The currently selected key in the collection (controlled).

slot?string | null

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit null value indicates that the local props completely override all props received from a parent.

Type
string | null
Default
tabs
style?StyleOrFunction<T>

The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.

Type
StyleOrFunction<T>
render?DOMRenderFunction<E, T>

Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if <button> is expected, you cannot render an <a>). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.

Type
DOMRenderFunction<E, T>
className?string

Merged with the part's classes.

AttributeType
data-focus?string

Present when focused is true.

data-orientation?string

Mirrors orientation.

TabsList

data-slot="tabs-list"

PropType
defaultSelectedKey?Key

The initial selected keys in the collection (uncontrolled).

isDisabled?boolean

Whether the TabList is disabled. Shows that a selection exists, but is not available in that circumstance.

onSelectionChange?(key: Key) → void

Handler that is called when the selection changes.

Parameters

key – Key

Returns
void
selectedKey?Key

The currently selected key in the collection (controlled).

variant?"default" | "line"

The visual style.

Type
"default" | "line"
Default
"default"
className?string

Merged with the part's classes.

AttributeType
data-focus?string

Present when focused is true.

data-orientationOrientation

Mirrors the orientation prop.

data-variant?string

Mirrors variant.

TabsTrigger

data-slot="tabs-trigger"

PropType
children?ChildrenOrFunction<T>

The children of the component. A function may be provided to alter the children based on component state.

Type
ChildrenOrFunction<T>
id?Key

The unique id of the tab.

isDisabled?boolean

Whether the tab is disabled.

style?StyleOrFunction<T>

The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.

Type
StyleOrFunction<T>
render?(props: DetailedHTMLProps<LinkWithRequiredHref, HTMLAnchorElement> | React.JSX.IntrinsicElements[Fallback], renderProps: T) → ReactElement

Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Note: You can check if 'href' in props in order to tell whether to render an <a> element. Requirements: - You must render the expected element type (e.g. if <a> is expected, you cannot render a <button>). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.

Type
(props: DetailedHTMLProps<LinkWithRequiredHref, HTMLAnchorElement> | React.JSX.IntrinsicElements[Fallback], renderProps: T) → ReactElement
Parameters

props – DetailedHTMLProps<LinkWithRequiredHref

HTMLAnchorElement> | React.JSX.IntrinsicElements[Fallback]

renderProps – T

Returns
ReactElement
className?string

Merged with the part's classes.

AttributeType
data-focus?string

Present when focused is true.

data-orientationOrientation

Mirrors the orientation prop.

TabsContent

data-slot="tabs-content"

PropType
children?ChildrenOrFunction<T>

The children of the component. A function may be provided to alter the children based on component state.

Type
ChildrenOrFunction<T>
id?Key

The unique id of the tab.

shouldForceMount?boolean

Whether to mount the tab panel in the DOM even when it is not currently selected. Inactive tab panels are inert and cannot be interacted with. They must be styled appropriately so this is clear to the user visually.

Default
false
style?StyleOrFunction<T>

The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.

Type
StyleOrFunction<T>
render?DOMRenderFunction<E, T>

Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if <button> is expected, you cannot render an <a>). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.

Type
DOMRenderFunction<E, T>
className?string

Merged with the part's classes.

AttributeType
data-focus?string

Present when focused is true.

data-orientationOrientation

Mirrors the orientation prop.