Tabs
Chassis Tabs organize content into multiple sections and let users navigate between them with full keyboard support.
Overview
Tabs organizes content into sections, showing one at a time. Unlike hand-wiring a Nav to a set of manually-toggled panels, Tabs handles keyboard navigation (arrow keys, Home/End), roving tab focus, and the role="tablist"/role="tab"/role="tabpanel" + aria-selected/aria-controls/aria-labelledby wiring between them. Switching tabs also cross-fades their panels automatically.
Compose a TabList of Tabs, followed by one TabPanel per tab. A tab and its panel are paired by a matching id — not by position, and not by React's own key (each still needs its own key, as with any list). Tabs/TabList/Tab/TabPanel mirror the names of the react-aria hooks they're built on (useTabList, useTab, useTabPanel).
import { Tabs, TabList, Tab, TabPanel } from '@chassis-ui/react'
export const Example = () => {
return (
<Tabs defaultSelectedKey="home">
<TabList aria-label="Example tabs">
<Tab id="home">Home</Tab>
<Tab id="profile">Profile</Tab>
<Tab id="contact">Contact</Tab>
</TabList>
<TabPanel id="home">
Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown
aliqua, retro synth master cleanse.
</TabPanel>
<TabPanel id="profile">
Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid.
Exercitation +1 labore velit, blog sartorial PBR leggings.
</TabPanel>
<TabPanel id="contact">
Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic
lomo retro fanny pack lo-fi farm-to-table readymade.
</TabPanel>
</Tabs>
)
}Variants and sizes
Pass variant="segments" to TabList for a segmented control instead of the default tabs. The variant was called pills before @chassis-ui/css 0.6: variant="pills" still renders it, and is deprecated.
import { Tabs, TabList, Tab, TabPanel } from '@chassis-ui/react'
export const Example = () => {
return (
<Tabs defaultSelectedKey="home">
<TabList aria-label="Segments example" variant="segments">
<Tab id="home">Home</Tab>
<Tab id="profile">Profile</Tab>
<Tab id="contact">Contact</Tab>
</TabList>
<TabPanel id="home">
Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown
aliqua, retro synth master cleanse.
</TabPanel>
<TabPanel id="profile">
Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid.
Exercitation +1 labore velit, blog sartorial PBR leggings.
</TabPanel>
<TabPanel id="contact">
Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic
lomo retro fanny pack lo-fi farm-to-table readymade.
</TabPanel>
</Tabs>
)
}Pass variant="underline" to mark the selected tab with a line under it, and size="sm" or size="lg" to scale the tabs of any variant.
import { Tabs, TabList, Tab, TabPanel } from '@chassis-ui/react'
export const Example = () => {
return (
<Tabs defaultSelectedKey="home">
<TabList aria-label="Underline example" variant="underline" size="sm">
<Tab id="home">Home</Tab>
<Tab id="profile">Profile</Tab>
<Tab id="contact">Contact</Tab>
</TabList>
<TabPanel id="home">
Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown
aliqua, retro synth master cleanse.
</TabPanel>
<TabPanel id="profile">
Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid.
Exercitation +1 labore velit, blog sartorial PBR leggings.
</TabPanel>
<TabPanel id="contact">
Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic
lomo retro fanny pack lo-fi farm-to-table readymade.
</TabPanel>
</Tabs>
)
}Disabled tabs
Add disabled to a Tab to remove it from keyboard navigation and prevent it from being selected.
import { Tabs, TabList, Tab, TabPanel } from '@chassis-ui/react'
export const Example = () => {
return (
<Tabs defaultSelectedKey="home">
<TabList aria-label="Example tabs with a disabled tab">
<Tab id="home">Home</Tab>
<Tab id="profile" disabled>
Profile
</Tab>
<Tab id="contact">Contact</Tab>
</TabList>
<TabPanel id="home">
Raw denim you probably haven't heard of them jean shorts Austin.
</TabPanel>
<TabPanel id="profile">This panel can't be reached — Profile is disabled.</TabPanel>
<TabPanel id="contact">
Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's.
</TabPanel>
</Tabs>
)
}Vertical
Pass orientation="vertical" to Tabs to stack the tabs. The list gets the flex-column class and aria-orientation="vertical", and the up and down arrow keys move between the tabs. The panels stay below the list unless the root is laid out: here className="d-flex align-items-start gap-md" puts them beside it.
import { Tabs, TabList, Tab, TabPanel } from '@chassis-ui/react'
export const Example = () => {
return (
<Tabs
className="d-flex align-items-start gap-md"
defaultSelectedKey="home"
orientation="vertical"
>
<TabList aria-label="Vertical example" variant="segments">
<Tab id="home">Home</Tab>
<Tab id="profile">Profile</Tab>
<Tab id="contact">Contact</Tab>
</TabList>
<TabPanel id="home">
Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown
aliqua, retro synth master cleanse.
</TabPanel>
<TabPanel id="profile">
Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid.
Exercitation +1 labore velit, blog sartorial PBR leggings.
</TabPanel>
<TabPanel id="contact">
Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic
lomo retro fanny pack lo-fi farm-to-table readymade.
</TabPanel>
</Tabs>
)
}Overflow
Wrap the TabList in a NavOverflow, inside the Tabs, to move the tabs that don't fit on one line into a "More" menu. Set keepVisible on a Tab that should never move.
Controlled selection
Pass selectedKey/onSelectionChange to Tabs to control which tab is selected externally, e.g. to sync it with the URL. Omit them (use defaultSelectedKey instead) to let Tabs manage selection internally.
API
Tabs
| Prop | Type | Default |
|---|---|---|
asChild? | boolean | — |
Render the single child element in place of this component's own element, with this
component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log
in</Link></Button>. The alternative to component for React Server Components — an element
can be passed from the server to this package's client components, a component reference
(component={Link}) can't. | ||
children? | ReactNode | — |
A TabList (containing Tab children) followed by one TabPanel per tab. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
component? | ElementType | — |
| Component used for the root node. Either a string to use a HTML element or a component. | ||
defaultSelectedKey? | Key | — |
| The initially selected tab's key (uncontrolled). | ||
disabledKeys? | Iterable<Key> | — |
| The keys of tabs that cannot be selected, focused, or otherwise interacted with. | ||
keyboardActivation? | "automatic" | "manual" | — |
Whether tabs are selected automatically on arrow-key focus ('automatic', the default) or
only on explicit activation — Enter/Space or click ('manual'). | ||
onSelectionChange? | ((key: Key) => void) | — |
| Callback fired when the selected tab changes. | ||
orientation? | "horizontal" | "vertical" | — |
The orientation of the tab list. 'vertical' stacks the tabs, with the flex-column class
on the list, and moves between them with the up and down arrow keys. The panels stay below
the list: to put them beside it, lay out the root with className. | ||
ref? | any | — |
selectedKey? | Key | — |
| The selected tab's key (controlled). | ||
TabList
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
Tab elements — read as data by Tabs to build the tab collection (see Tabs.tsx). Not
rendered directly. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
size? | "sm" | "lg" | — |
| Size the component sm or lg. | ||
variant? | { 'tabs' | 'segments' | 'underline' | 'pills' } | tabs |
Set the tab list variant to tabs, to segments (a segmented control) or to underline, which
underlines the selected tab. 'pills' is the former name of 'segments': deprecated, it
renders the same. | ||
Tab
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
| Label content for the tab. Must be a plain string for it to be used as the tab's accessible name when the rendered content itself doesn't expose one (e.g. an icon-only tab). | ||
disabled? | boolean | — |
| Prevents the tab from being selected, focused, or otherwise interacted with. | ||
id | Key | — |
Identifies this tab and pairs it with the TabPanel of the same id. | ||
keepVisible? | boolean | — |
Keeps the tab in the list inside a NavOverflow, whatever the width. | ||
TabPanel
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
Content of the panel, shown while the Tab of the same id is selected. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
id | Key | — |
Pairs this panel with the Tab of the same id. | ||