Components Navigation
Tabs
StableSwitches between related panels of content within the same page.
Use Tabs to divide one page’s content into peer sections, such as a template’s overview, assignees and history. Panels should not depend on each other. For moving between pages use TabBar, Sidebar or links; for a sequence of steps use Stepper.
Overview
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@bindshq/stance-react';
export function Basic() {
return (
<Tabs defaultValue="overview" style={{ width: '100%' }}>
<TabsList aria-label="Template sections">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="assignees">Assignees</TabsTrigger>
<TabsTrigger value="history">History</TabsTrigger>
</TabsList>
<TabsContent value="overview">
The Sales signature is live for 48 people across 3 teams.
</TabsContent>
<TabsContent value="assignees">Assign the template to users or whole teams.</TabsContent>
<TabsContent value="history">Version 7 was published on 4 November 2026.</TabsContent>
</Tabs>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@bindshq/stance-react';Platforms
Tabs has native counterparts. They share Stance tokens and intent, and use each platform’s own controls and conventions. The live previews on this page are the web implementation.
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@bindshq/stance-react';Playground
<Tabs />Examples
Pill variant
A segmented look for switching views of the same data.
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@bindshq/stance-react';
export function Pill() {
return (
<Tabs defaultValue="month" variant="pill">
<TabsList aria-label="Reporting period">
<TabsTrigger value="week">Week</TabsTrigger>
<TabsTrigger value="month">Month</TabsTrigger>
<TabsTrigger value="quarter">Quarter</TabsTrigger>
</TabsList>
<TabsContent value="week">1,204 signatures deployed this week.</TabsContent>
<TabsContent value="month">4,918 signatures deployed this month.</TabsContent>
<TabsContent value="quarter">13,560 signatures deployed this quarter.</TabsContent>
</Tabs>
);
}import { Icon, Tabs, TabsContent, TabsList, TabsTrigger } from '@bindshq/stance-react';
export function WithIconsAndCounts() {
return (
<Tabs defaultValue="active" style={{ width: '100%' }}>
<TabsList aria-label="Users">
<TabsTrigger value="active" icon={<Icon name="users" />} badge={48}>
Active
</TabsTrigger>
<TabsTrigger value="invited" icon={<Icon name="mail" />} badge={5}>
Invited
</TabsTrigger>
<TabsTrigger value="suspended" icon={<Icon name="lock" />} disabled>
Suspended
</TabsTrigger>
</TabsList>
<TabsContent value="active">48 people have an active signature.</TabsContent>
<TabsContent value="invited">5 invitations are waiting to be accepted.</TabsContent>
<TabsContent value="suspended">No suspended users.</TabsContent>
</Tabs>
);
}Vertical
Settings-style navigation; becomes horizontal below 640px.
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@bindshq/stance-react';
export function Vertical() {
return (
<Tabs defaultValue="profile" orientation="vertical" style={{ width: '100%' }}>
<TabsList aria-label="Settings">
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="organisation">Organisation</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
</TabsList>
<TabsContent value="profile">Your name, photo and contact details.</TabsContent>
<TabsContent value="organisation">Company name, domains and branding.</TabsContent>
<TabsContent value="security">Two-factor authentication and sessions.</TabsContent>
<TabsContent value="billing">Plan, invoices and payment method.</TabsContent>
</Tabs>
);
}Manual activation
Arrow keys move focus only; Enter or Space selects. Use when panels fetch data.
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@bindshq/stance-react';
export function Manual() {
return (
<Tabs defaultValue="exchange" activationMode="manual" style={{ width: '100%' }}>
<TabsList aria-label="Integrations">
<TabsTrigger value="exchange">Exchange</TabsTrigger>
<TabsTrigger value="google">Google Workspace</TabsTrigger>
<TabsTrigger value="azure">Azure AD</TabsTrigger>
</TabsList>
<TabsContent value="exchange">Exchange relay is connected.</TabsContent>
<TabsContent value="google">Connect Google Workspace to sync users.</TabsContent>
<TabsContent value="azure">Azure AD sync runs every 6 hours.</TabsContent>
</Tabs>
);
}API reference
Tabs
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | None | Controlled selected tab. |
defaultValue | string | None | Initially selected tab when uncontrolled. |
onValueChange | (value: string) => void | None | Called when the selected tab changes. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout and arrow-key axis. |
activationMode | 'automatic' | 'manual' | 'automatic' | Whether focusing a tab selects it. |
variant | 'line' | 'pill' | 'line' | Underline or filled indicator. |
size | 'sm' | 'md' | 'md' | Trigger height. |
TabsList
| Prop | Type | Default | Description |
|---|---|---|---|
loop | boolean | true | Wrap focus from last to first. |
fullWidth | boolean | false | Stretch triggers to fill the width. |
aria-label | string | None | Name for the tab set; required when a page has several. |
TabsTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
value* required | string | None | Identifies the tab and its panel. |
icon | ReactNode | None | Icon before the label. |
badge | ReactNode | None | Trailing count. |
disabled | boolean | None | Skips the tab in keyboard navigation. |
TabsContent
| Prop | Type | Default | Description |
|---|---|---|---|
value* required | string | None | Matches a trigger value. |
forceMount | boolean | false | Keep the panel mounted while hidden to preserve its state. |
Accessibility
- Implements the WAI-ARIA tabs pattern: tablist, tab and tabpanel roles with aria-selected, aria-controls and aria-labelledby.
- Only the selected tab is in the Tab order; arrow keys move between tabs and disabled tabs are skipped.
- Panels are focusable (tabindex 0) so keyboard users can reach content that has no interactive elements.
- High-contrast mode thickens the indicator and fills pill tabs so the selection never relies on subtle tints.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves focus into the tab list, then to the active panel. |
| ← → | Moves between tabs (horizontal). |
| ↑ ↓ | Moves between tabs (vertical). |
| Home End | First / last tab. |
| Enter Space | Selects the focused tab (manual mode). |
Usage guidelines
Do
- Use short, parallel labels.
- Keep the most used panel first.
Don’t
- Nest tabs inside tabs.
- Use tabs for steps that must be completed in order.