Tabs
Composable layout tab container for switching between views.
The Tabs primitive provides a modular, keyboard-navigable component structure for organizing content into discrete panel directories.
Quick Start
Compose tab buttons and panels using subcomponents:
tsx
import React, { useState } from 'react'
import { Tabs } from 'boltdocs/primitives'
export default function CustomTabs() {
const [activeTab, setActiveTab] = useState('tab-1')
return (
<Tabs>
<Tabs.List className="border-b border-subtle mb-4">
<Tabs.Item
id="tab-1"
selected={activeTab === 'tab-1'}
onClick={() => setActiveTab('tab-1')}
className="px-4 py-2 text-sm font-semibold data-[selected=true]:text-primary-500 border-b-2 border-transparent data-[selected=true]:border-primary-500"
>
Overview
</Tabs.Item>
<Tabs.Item
id="tab-2"
selected={activeTab === 'tab-2'}
onClick={() => setActiveTab('tab-2')}
className="px-4 py-2 text-sm font-semibold data-[selected=true]:text-primary-500 border-b-2 border-transparent data-[selected=true]:border-primary-500"
>
API Reference
</Tabs.Item>
</Tabs.List>
<Tabs.Content>
{activeTab === 'tab-1' && <div>Overview content panel...</div>}
{activeTab === 'tab-2' && <div>Detailed API reference description...</div>}
</Tabs.Content>
</Tabs>
)
}
Composable Subcomponents
The Tabs primitive provides the following namespaces for layout customization:
| Component | HTML Tag | Description | Props |
|---|---|---|---|
Tabs.Root / Tabs | <div> | Main wrapper of the top-level tabs block. | ComponentBaseProps |
Tabs.List | <div> (tablist) | Contains the tab category triggers. | ComponentBaseProps |
Tabs.Item | <button> (tab) | The individual tab button selector cell. | Tabs.Item Props |
Tabs.Content | <div> (tabpanel) | View container wrapping the active content sections. | ComponentBaseProps |
Tabs.Indicator | <div> | An absolutely-positioned highlight line showing the active tab. | Tabs.Indicator Props |
Component Props
ComponentBaseProps (Common)
| Property | Type | Default | Description |
|---|---|---|---|
children | ReactNode | undefined | Child content elements. |
className | string | undefined | Custom utility CSS classes. |
style | CSSProperties | undefined | Inline style settings. |
Tabs.Item Props
| Property | Type | Default | Description |
|---|---|---|---|
id | string | Required | Unique selector key matching the tab content panel. |
selected | boolean | false | Active highlighted selection state. |
disabled | boolean | false | Prevents selection click triggers. |
onClick | () => void | undefined | Active selection callback handler. |
onKeyDown | (e: KeyboardEvent) => void | undefined | Key press activation callback handler. |
Tabs.Indicator Props
| Property | Type | Default | Description |
|---|---|---|---|
style | CSSProperties | undefined | Dynamic coordinate mapping styles (e.g., transform, width). |
Customization & states
Tabs is a self-contained primitive (not React Aria) and fully style-neutral. Tabs.Item is a <button role="tab"> that exposes its selection through data-selected and aria-selected, so the active tab is styled from CSS with Tailwind v4 data-[selected=true]: variants. Tabs.Indicator is the themed moving highlight — position it with style and color it through className.
| State / Slot | Mechanism |
|---|---|
data-selected | On Tabs.Item, "true" / "false" — style with data-[selected=true]: |
aria-selected | Accessibility mirror of the selection state on Tabs.Item |
Tabs.Item className | Passthrough; tab visuals (color, borders, padding) from the theme |
Tabs.Indicator className | Passthrough; color and height of the active underline |
Tabs.Indicator style | Dynamic positioning (transform, width) of the highlight |
Tabs.List / Tabs.Content | className passthrough, no state |
Tailwind v4:
tsx
<Tabs.Item
id="overview"
selected={activeTab === 'overview'}
className="px-4 py-2 text-sm border-b-2 border-transparent text-muted
data-[selected=true]:text-primary-500 data-[selected=true]:border-primary-500"
>
Overview
</Tabs.Item>
Last updated on October 18, 2026