Tabs
Overviewimport { Tabs, TabsContent, TabsDivider, TabsList, TabsTrigger } from '@nili/ui';This is the library’s own overview page, rendered live. Switch the language or the theme in the header and it follows.
Horizontal
The default. label names the tab set — without it a screen reader announces “tab list” and nothing else, which is useless on a page with three of them.
Your name, photo and bio.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@nili/ui"
export function Example() {
return (
<Tabs defaultValue="profile">
<TabsList label={t('settings.label')}>
<TabsTrigger value="profile">{t('tab.profile')}</TabsTrigger>
<TabsTrigger value="security">{t('tab.security')}</TabsTrigger>
<TabsTrigger value="alerts">{t('tab.alerts')}</TabsTrigger>
</TabsList>
<TabsContent value="profile">Your name, photo and bio.</TabsContent>
<TabsContent value="security">Password and two-factor.</TabsContent>
<TabsContent value="alerts">What we email you about.</TabsContent>
</Tabs>
)
}Icons and badges
startIcon and endIcon are logical, so they lead in both directions. badge takes any node — a bare count or a full Badge.
Your name, photo and bio.
import { Tabs, TabsList, TabsTrigger, TabsContent, Badge, Icon } from "@nili/ui"
import { RiUser3Line, RiNotification3Line } from "@remixicon/react"
export function Example() {
return (
<Tabs defaultValue="profile">
<TabsList label="Account settings">
<TabsTrigger value="profile" startIcon={<Icon icon={RiUser3Line} />}>
{t('tab.profile')}
</TabsTrigger>
<TabsTrigger
value="alerts"
startIcon={<Icon icon={RiNotification3Line} />}
badge={<Badge size="small" color="red">3</Badge>}
>
{t('tab.alerts')}
</TabsTrigger>
</TabsList>
…
</Tabs>
)
}Stretch
stretch makes the tabs share the width instead of hugging their labels — right for a segmented control inside a card or a mobile sheet.
The last seven days.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@nili/ui"
export function Example() {
return (
<Tabs defaultValue="week">
<TabsList stretch label={t('range.label')}>
<TabsTrigger value="day">{t('range.day')}</TabsTrigger>
<TabsTrigger value="week">{t('range.week')}</TabsTrigger>
<TabsTrigger value="month">{t('range.month')}</TabsTrigger>
</TabsList>
…
</Tabs>
)
}Vertical
appearance applies to vertical tabs only — the horizontal menu has no style axis in the design. TabsDivider rules off a group of tabs.
Your name, photo and bio.
Your name, photo and bio.
import { Tabs, TabsList, TabsTrigger, TabsContent, TabsDivider } from "@nili/ui"
export function Example() {
return (
<Tabs defaultValue="profile" orientation="vertical" appearance="card">
<TabsList label="Settings">
<TabsTrigger value="profile">{t('tab.profile')}</TabsTrigger>
<TabsTrigger value="security">{t('tab.security')}</TabsTrigger>
<TabsDivider />
<TabsTrigger value="usage">Usage</TabsTrigger>
</TabsList>
…
</Tabs>
)
}Activation mode and keepMounted
automatic selects a tab as focus reaches it — the ARIA default, right when panels are cheap. Use manual when a tab loads data: arrowing past four tabs should not fire four requests. keepMounted preserves scroll position and form state in a hidden panel.
Arrow between the tabs — with `manual`, nothing changes until you press Enter or Space.
This panel stays mounted while hidden.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@nili/ui"
export function Example() {
return (
<Tabs defaultValue="overview" activationMode="manual">
<TabsList label={t('report.label')}>
<TabsTrigger value="overview">{t('report.overview')}</TabsTrigger>
<TabsTrigger value="chart">{t('report.chart')}</TabsTrigger>
</TabsList>
<TabsContent value="overview">Cheap to render.</TabsContent>
{/* expensive to rebuild — keep it mounted */}
<TabsContent value="chart" keepMounted>
<RevenueChart />
</TabsContent>
</Tabs>
)
}Tabs
extends Omit< ComponentPropsWithoutRef<'div'>, 'onChange' | 'defaultValue' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| value | string | — | Selected tab. Controlled. |
| defaultValue | string | ||
| onValueChange | (value: string) => void | — | |
| orientation | horizontal | vertical | horizontal | |
| appearance | card | list | list | Figma's `Style (Card | List)`. Vertical only — the horizontal tab menu has no style axis in the design. |
| activationMode | automatic | manual | automatic |
TabsContent
extends ComponentPropsWithoutRef<'div'>
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| value* | string | — | |
| keepMounted | boolean | false | Keep the panel mounted while hidden. Off by default: an unmounted panel cannot run effects or hold stale data. Turn it on to preserve scroll position, form state or a chart that is expensive to rebuild. |
TabsDivider
TabsList
extends ComponentPropsWithoutRef<'div'>
Variant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| stretch | boolean | false | Tabs share the width instead of hugging their labels. |
| orientation | horizontal | vertical | horizontal | |
| appearance | card | list | list |
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| label | string | — | Accessible name for the tab set, e.g. `'Account settings'`. |
TabsTrigger
extends Omit< ComponentPropsWithoutRef<'button'>, 'value' >
Variant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| orientation | horizontal | vertical | horizontal | |
| appearance | card | list | list | |
| selected | 'true' | 'false' | false |
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| disabled | boolean | — | |
| required | boolean | — | |
| readOnly | boolean | — | |
| value* | string | — | Ties the tab to its panel. Must be unique within one `Tabs`. |
| defaultValue | string | — | |
| name | string | — | |
| aria-label | string | — | |
| startIcon | ReactNode | — | Figma's "Left Icon". Logical, so it leads in both directions. |
| endIcon | ReactNode | — | Figma's "Right Icon". |
| badge | ReactNode | — | Figma's "Number" — a count or a `<Badge>`, after the label. |