Tabs
Tab navigation for switching between related views or panels.
Tabs is backed by @areia/slots and wires accessible tab semantics, keyboard navigation, an animated indicator, optional content panels, and horizontal overflow scrolling after mount.
Ilha bind:group
Inside an Ilha island (including nested child islands), bind the active tab value:
const Panel = ilha
.state("tab", "overview")
.render(({ state }) => (
<Tabs variant="segmented" bind:group={state.tab}>
<Tabs.List>
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
</Tabs>
));
value + onValueChange remains supported. Requires ilha ^0.8.0.
Segmented
Underline
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<div class="flex flex-col gap-6">
<div>
<p class="mb-2 text-sm text-areia-subtle">Segmented</p>
<Tabs
tabs={[
{ value: "overview", label: "Overview" },
{ value: "analytics", label: "Analytics" },
{ value: "settings", label: "Settings" },
]}
selectedValue="overview"
/>
</div>
<div>
<p class="mb-2 text-sm text-areia-subtle">Underline</p>
<Tabs
variant="underline"
tabs={[
{ value: "overview", label: "Overview" },
{ value: "analytics", label: "Analytics" },
{ value: "settings", label: "Settings" },
]}
selectedValue="overview"
/>
</div>
</div>
));Import
import { Tabs } from "areia";
Usage
Call Tabs(...) for interactive behavior. Prefer composing Tabs.List, Tabs.Trigger, and Tabs.Content as children; the tabs array remains available as a shortcut. It initializes the tabs controller, syncs ARIA and data-state attributes, and emits tabs:change when the active tab changes.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<Tabs selectedValue="overview">
<Tabs.List>
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
</Tabs>
));Use Tabs.Static(...) for static markup or when you want to initialize behavior yourself. For most application usage, call Tabs(...).
Examples
Segmented
A pill-shaped indicator slides between tabs on a subtle background.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<Tabs
variant="segmented"
tabs={[
{ value: "tab-1", label: "Tab 1" },
{ value: "tab-2", label: "Tab 2" },
{ value: "tab-3", label: "Tab 3" },
]}
selectedValue="tab-1"
/>
));Underline
The underline variant uses a bottom border and primary-colored indicator.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<Tabs
variant="underline"
tabs={[
{ value: "tab-1", label: "Tab 1" },
{ value: "tab-2", label: "Tab 2" },
{ value: "tab-3", label: "Tab 3" },
]}
selectedValue="tab-1"
/>
));Small Size
Use size="sm" for compact tabs inside toolbars and filter rows.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<div class="flex flex-col gap-6">
<Tabs
size="sm"
tabs={[
{ value: "open", label: "Open" },
{ value: "closed", label: "Closed" },
{ value: "all", label: "All" },
]}
selectedValue="open"
/>
<Tabs
variant="underline"
size="sm"
tabs={[
{ value: "daily", label: "Daily" },
{ value: "weekly", label: "Weekly" },
{ value: "monthly", label: "Monthly" },
]}
selectedValue="daily"
/>
</div>
));Many Tabs
Segmented tabs scroll horizontally when there are more items than available space. Mouse users can drag the tab list to scroll.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<div class="w-full max-w-md">
<Tabs
tabs={[
{ value: "overview", label: "Overview" },
{ value: "analytics", label: "Analytics" },
{ value: "reports", label: "Reports" },
{ value: "notifications", label: "Notifications" },
{ value: "settings", label: "Settings" },
{ value: "billing", label: "Billing" },
{ value: "security", label: "Security" },
{ value: "integrations", label: "Integrations" },
]}
selectedValue="overview"
/>
</div>
));Change Events
Use onValueChange to respond when the active tab changes.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha
.state("activeTab", "overview")
.render(({ state }) => (
<div class="space-y-4">
<Tabs
tabs={[
{ value: "overview", label: "Overview" },
{ value: "usage", label: "Usage" },
{ value: "settings", label: "Settings" },
]}
selectedValue={state.activeTab()} => {
state.activeTab(value);
}}
/>
<p class="text-sm text-areia-subtle">
Active tab: <code>{state.activeTab}</code>
</p>
</div>
));Content Panels
Compose Tabs.Content children for panel content. This keeps panel JSX colocated with each tab value.
Project health and key metrics.
Recent project activity appears here.
Workspace preferences and access controls.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<Tabs selectedValue="overview">
<Tabs.List>
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="activity">Activity</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="overview">
<div class="rounded-lg bg-areia-surface-muted p-4 text-sm">
Project health and key metrics.
</div>
</Tabs.Content>
<Tabs.Content value="activity">
<div class="rounded-lg bg-areia-surface-muted p-4 text-sm">
Recent project activity appears here.
</div>
</Tabs.Content>
<Tabs.Content value="settings">
<div class="rounded-lg bg-areia-surface-muted p-4 text-sm">
Workspace preferences and access controls.
</div>
</Tabs.Content>
</Tabs>
));Data Shortcut
Use the tabs array for concise data-driven tabs. Pass content on each item to generate content panels.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<Tabs
selectedValue="overview"
tabs={[
{
value: "overview",
label: "Overview",
content: "Overview panel",
},
{
value: "settings",
label: "Settings",
content: "Settings panel",
},
]}
/>
));Disabled Item
Disable individual tabs with disabled: true.
import ilha from "ilha";
import { Tabs } from "areia";
export default ilha.render(() => (
<Tabs
tabs={[
{ value: "profile", label: "Profile" },
{ value: "team", label: "Team" },
{ value: "billing", label: "Billing", disabled: true },
]}
selectedValue="profile"
/>
));API
Tabs(input)
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | TabsItem[] | [] | Items rendered as tab triggers. |
children | unknown | — | Composed Tabs.List, Tabs.Trigger, and Tabs.Content markup. Takes priority over tabs. |
value | string | — | Initial active value. |
selectedValue | string | — | Initial active value for uncontrolled usage. |
defaultValue | string | — | Alias for the initial active value. |
variant | "segmented" | "underline" | "segmented" | Visual style. |
size | "base" | "sm" | "base" | Tab size. |
activationMode | "auto" | "manual" | "manual" | Keyboard activation mode passed to the tabs controller. |
activateOnFocus | boolean | false | Uses automatic arrow-key activation when true. |
class | string | — | Additional classes for the root. |
className | string | — | Alias for class. |
listClass | string | — | Additional classes for the tab list. |
listClassName | string | — | Alias for listClass. |
contentClass | string | — | Additional classes for every generated content panel. |
contentClassName | string | — | Alias for contentClass. |
indicatorClass | string | — | Additional classes for the active indicator. |
indicatorClassName | string | — | Alias for indicatorClass. |
onValueChange | (value: string) => void | — | Called when the active tab changes. |
TabsItem
| Property | Type | Required | Description |
|---|---|---|---|
value | string | Yes | Unique tab value. |
label | unknown | No | Visible tab label. |
children | unknown | No | Custom tab content. Takes priority over label. |
content | unknown | No | Panel content for this tab. |
disabled | boolean | No | Disables the tab trigger. |
class | string | No | Additional classes for the trigger. |
className | string | No | Alias for class. |
contentClass | string | No | Additional classes for this tab's panel. |
contentClassName | string | No | Alias for contentClass. |
Slots
Tabs renders these data slots:
tabstabs-listtabs-triggertabs-contenttabs-indicator
The root and triggers receive attributes from the tabs controller:
data-valuedata-statearia-selectedaria-controlstabindex