The value of the tab that opens first. Match it to a real TabItem: a value
no tab carries selects nothing and shows no panel, instead of falling back to
the first tab. A stored syncKey selection overrides it on load.
Tabs
Tabs renders a row of tab buttons over a single visible panel, and each
TabItem supplies one tab's label and its content. The tab strip is a real ARIA
tablist: arrow keys move between tabs and switch the panel, wrapping from the
last tab back to the first, Home jumps to the first tab, End to the last,
and focus follows the selection. Every example below shows the live component
first, then the exact MDX that produced it.
Quick Start
Two tabs and nothing else — the first TabItem is active on load.
React uses a virtual DOM and JSX syntax for building user interfaces.
<Tabs>
<TabItem value="react" label="React">
React uses a virtual DOM and JSX syntax for building user interfaces.
</TabItem>
<TabItem value="vue" label="Vue">
Vue provides a progressive framework with an approachable template syntax.
</TabItem>
</Tabs>Adding Options
Add a third tab and set defaultValue to the value of whichever tab should
open first.
Svelte shifts work to compile time, producing vanilla JavaScript.
<Tabs defaultValue="svelte">
<TabItem value="react" label="React">
React uses a virtual DOM and JSX syntax.
</TabItem>
<TabItem value="vue" label="Vue">
Vue provides a progressive framework with reactive data binding.
</TabItem>
<TabItem value="svelte" label="Svelte">
Svelte shifts work to compile time, producing vanilla JavaScript.
</TabItem>
</Tabs>Advanced
syncKey writes the selected value to localStorage under tabs-<syncKey>, so
a reader who picks pnpm here sees pnpm on the next page that uses the same key.
Five tabs also show the other half of the strip's behavior: it scrolls sideways
instead of wrapping onto a second row, so a narrow screen keeps one tidy line.
npm install next
<Tabs defaultValue="npm" syncKey="package-manager">
<TabItem value="npm" label="npm">
`npm install next`
</TabItem>
<TabItem value="yarn" label="yarn">
`yarn add next`
</TabItem>
<TabItem value="pnpm" label="pnpm">
`pnpm add next`
</TabItem>
<TabItem value="bun" label="bun">
`bun add next`
</TabItem>
<TabItem value="deno" label="deno">
`deno add npm:next`
</TabItem>
</Tabs>Reuse package-manager on another page and both groups need the same value
set. A stored value that no tab on the page matches leaves the group with
nothing selected and no panel showing.
Options
Tabs
defaultValuestringDefault: first tab's valuesyncKeystringKey used to save and restore the selection in localStorage as
tabs-<syncKey>. Reading and writing are wrapped in try/catch, so blocked
storage leaves the tabs working. Omit it and the choice resets on every visit.
childrenReactNoderequiredOne or more TabItem elements. A child without both value and label is
skipped, so it renders no tab button and no panel. Tabs keep their source
order, and the strip scrolls horizontally once the labels outgrow the content
width.
TabItem
valuestringrequiredIdentifier for the tab. Keep it unique within one Tabs group — it pairs the
tab button with its panel and is the value defaultValue and syncKey match
against.
labelstringrequiredText shown on the tab button.
childrenReactNoderequiredPanel content. It is rendered only while this tab is the active one, so a hidden panel's markup never reaches the page.
Two Tabs groups on the same page can share the same value strings without
colliding. Each group generates its own React id prefix, and the id,
aria-controls, and aria-labelledby attributes are built from it.