Tabs
Tab component for switching between views within a panel. Uses a compound component pattern with Tabs, TabList, Tab, and TabPanel. Supports multiple visual variants, horizontal and vertical orientations, closable tabs, and full keyboard navigation.
Live Preview
Import
import { Tabs, TabList, Tab, TabPanel } from 'entangle-ui';Usage
<Tabs defaultValue="properties"> <TabList> <Tab value="properties">Properties</Tab> <Tab value="materials">Materials</Tab> <Tab value="modifiers">Modifiers</Tab> </TabList> <TabPanel value="properties">Properties panel content</TabPanel> <TabPanel value="materials">Materials panel content</TabPanel> <TabPanel value="modifiers">Modifiers panel content</TabPanel></Tabs>Controlled vs Uncontrolled
// Controlledconst [activeTab, setActiveTab] = useState('properties');
<Tabs value={activeTab} onChange={setActiveTab}> <TabList> <Tab value="properties">Properties</Tab> <Tab value="materials">Materials</Tab> </TabList> <TabPanel value="properties">...</TabPanel> <TabPanel value="materials">...</TabPanel></Tabs>
// Uncontrolled<Tabs defaultValue="properties"> ...</Tabs>Variants
Underline
Bottom border indicator, the default style similar to VS Code tabs.
<Tabs variant="underline" defaultValue="scene"> <TabList> <Tab value="scene">Scene</Tab> <Tab value="world">World</Tab> </TabList> <TabPanel value="scene">Scene settings</TabPanel> <TabPanel value="world">World settings</TabPanel></Tabs>Pills
Filled background on the active tab.
<Tabs variant="pills" defaultValue="edit"> <TabList> <Tab value="edit">Edit</Tab> <Tab value="object">Object</Tab> <Tab value="sculpt">Sculpt</Tab> </TabList> <TabPanel value="edit">Edit mode</TabPanel> <TabPanel value="object">Object mode</TabPanel> <TabPanel value="sculpt">Sculpt mode</TabPanel></Tabs>Use pillsFrame={false} to remove the surrounding border/background from the pills tab list.
<Tabs variant="pills" pillsFrame={false} defaultValue="a"> ...</Tabs>Enclosed
Bordered active tab with a connected panel appearance.
<Tabs variant="enclosed" defaultValue="output"> <TabList> <Tab value="output">Output</Tab> <Tab value="console">Console</Tab> </TabList> <TabPanel value="output">Output content</TabPanel> <TabPanel value="console">Console content</TabPanel></Tabs>Sizes
<Tabs size="sm" defaultValue="a">...</Tabs><Tabs size="md" defaultValue="a">...</Tabs><Tabs size="lg" defaultValue="a">...</Tabs>| Size | Tab Height | Use case |
|---|---|---|
sm | 24px | Ultra-compact panels |
md | 28px | Compact default |
lg | 32px | Comfortable navigation |
Full Width
Make tabs fill the available width equally.
<Tabs fullWidth defaultValue="a"> <TabList> <Tab value="a">Tab A</Tab> <Tab value="b">Tab B</Tab> <Tab value="c">Tab C</Tab> </TabList> ...</Tabs>Vertical Orientation
<Tabs orientation="vertical" defaultValue="general"> <TabList> <Tab value="general">General</Tab> <Tab value="render">Render</Tab> <Tab value="output">Output</Tab> </TabList> <TabPanel value="general">General settings</TabPanel> <TabPanel value="render">Render settings</TabPanel> <TabPanel value="output">Output settings</TabPanel></Tabs>Tabs with Icons
<Tabs defaultValue="scene"> <TabList> <Tab value="scene" icon={<SceneIcon />}> Scene </Tab> <Tab value="render" icon={<RenderIcon />}> Render </Tab> </TabList> ...</Tabs>Closable Tabs
<Tabs defaultValue="file1"> <TabList> <Tab value="file1" closable onClose={v => closeFile(v)}> main.ts </Tab> <Tab value="file2" closable onClose={v => closeFile(v)}> styles.css </Tab> </TabList> ...</Tabs>Disabled Tabs
<Tabs defaultValue="a"> <TabList> <Tab value="a">Active</Tab> <Tab value="b" disabled> Disabled </Tab> <Tab value="c">Another</Tab> </TabList> ...</Tabs>Keep Mounted
By default, inactive panels are unmounted. Use keepMounted to preserve their state.
<TabPanel value="editor" keepMounted> <CodeEditor /> {/* State preserved when switching tabs */}</TabPanel>Props
Tabs (Root)
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Currently active tab value (controlled). |
defaultValue | string | — | Default active tab (uncontrolled). |
variant | 'underline' | 'pills' | 'enclosed' | 'underline' | Visual variant of the tab list. |
size | 'sm' | 'md' | 'lg' | 'md' | Tab size. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Orientation of the tab list. |
fullWidth | boolean | false | Whether tabs fill the available width equally. |
pillsFrame | boolean | true | Whether pills variant renders a framed container. |
children | ReactNode | — | TabList and TabPanel components. |
onChange | (value: string) => void | — | Callback when the active tab changes. |
className | string | — | Additional CSS class names. |
style | CSSProperties | — | Inline styles. |
testId | string | — | Test identifier for automated testing. |
ref | Ref<HTMLDivElement> | — | Ref to the root element. |
Tab
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Unique value identifying this tab. Must match a TabPanel value. |
children | ReactNode | — | Tab label content. |
icon | ReactNode | — | Icon displayed before the label. |
disabled | boolean | false | Whether this tab is disabled. |
closable | boolean | false | Whether this tab shows a close button. |
onClose | (value: string) => void | — | Callback when the close button is clicked. |
className | string | — | Additional CSS class names. |
testId | string | — | Test identifier for automated testing. |
TabPanel
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Value matching a Tab value. |
children | ReactNode | — | Panel content, only rendered when active. |
keepMounted | boolean | false | Whether to keep the panel mounted when inactive (preserves state). |
className | string | — | Additional CSS class names. |
testId | string | — | Test identifier for automated testing. |
Accessibility
- Tabs use
role="tablist",role="tab", androle="tabpanel"with proper ARIA relationships aria-selectedmarks the active tabaria-controlsandaria-labelledbylink tabs to their panelsaria-orientationreflects horizontal or vertical layout- Keyboard navigation:
- Arrow Left/Right (horizontal) or Arrow Up/Down (vertical): Move between tabs
- Home/End: Move to first/last tab
- Enter/Space: Activate the focused tab
- Disabled tabs are skipped during keyboard navigation
- Tab panels are hidden with
display: nonewhen keepMounted is true and the tab is inactive