<Tabs
variant="underline"
size="md"
defaultValue="overview"
>
<Stack gap={4} width="320px" maxWidth="100%">
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
<Tabs.Item value="settings">Settings</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="overview">Project summary and current status.</Tabs.Panel>
<Tabs.Panel value="activity">Recent changes to the project.</Tabs.Panel>
<Tabs.Panel value="settings">Members, permissions, and integrations.</Tabs.Panel>
</Stack>
</Tabs>Features
- Pairs each tab with a panel by
value. - Supports three variants and three sizes.
- Supports vertical lists and equal-width items.
- Can contain text,
icon, orendSlot. - Supports disabled items and controlled selection.
- Works as a form field with
name, using native radio inputs.
Anatomy
Tabs holds the selection, List holds the tabs, each Item is one tab, and each Panel shows the content for the item with the matching value.
<Tabs>
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="overview">Project summary and current status.</Tabs.Panel>
<Tabs.Panel value="activity">Recent changes to the project.</Tabs.Panel>
</Tabs>Variants
The default underline variant marks the selected tab with a line. pill highlights it with a background, and segment places the tabs on a track, like a segmented control.
<Tabs variant="underline" defaultValue="overview">
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
</Tabs.List>
</Tabs>
<Tabs variant="pill" defaultValue="overview">
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
</Tabs.List>
</Tabs>
<Tabs variant="segment" defaultValue="overview">
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
</Tabs.List>
</Tabs>Color
On the underline variant, color="primary" colors the selection indicator.
<Tabs color="primary" defaultValue="overview">
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
</Tabs.List>
</Tabs>Sizes
Tabs come in three sizes: sm, md, and lg. The default is md.
<Tabs size="sm" defaultValue="preview">
<Tabs.List>
<Tabs.Item value="preview">Preview</Tabs.Item>
<Tabs.Item value="code">Code</Tabs.Item>
</Tabs.List>
</Tabs>
<Tabs size="md" defaultValue="preview">
<Tabs.List>
<Tabs.Item value="preview">Preview</Tabs.Item>
<Tabs.Item value="code">Code</Tabs.Item>
</Tabs.List>
</Tabs>
<Tabs size="lg" defaultValue="preview">
<Tabs.List>
<Tabs.Item value="preview">Preview</Tabs.Item>
<Tabs.Item value="code">Code</Tabs.Item>
</Tabs.List>
</Tabs>Equal Widths
Set equalWidthItems to give every tab the same width.
<Tabs variant="segment" equalWidthItems defaultValue="draft">
<Tabs.List>
<Tabs.Item value="draft">Draft</Tabs.Item>
<Tabs.Item value="review">In review</Tabs.Item>
<Tabs.Item value="published">Published</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="draft">Projects being prepared.</Tabs.Panel>
<Tabs.Panel value="review">Projects awaiting approval.</Tabs.Panel>
<Tabs.Panel value="published">Projects available to your team.</Tabs.Panel>
</Tabs>Vertical Tabs
Set orientation="vertical" to stack the tabs; only the underline variant supports it. Tabs has no built-in layout, so arrange the list and panels with Stack.
<Tabs orientation="vertical" defaultValue="profile">
<Stack direction="row" gap={6}>
<Tabs.List>
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="security">Security</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">Update your name and profile details.</Tabs.Panel>
<Tabs.Panel value="security">Manage passwords and active sessions.</Tabs.Panel>
</Stack>
</Tabs>Icons
Add icon to show an icon before the label. Icon-only items need ariaLabel instead of children.
import { Eye, Heart } from "@vortexlabs/vortex-icons";
<Tabs variant="pill" defaultValue="preview">
<Tabs.List>
<Tabs.Item value="preview" icon={<Eye />}>Preview</Tabs.Item>
<Tabs.Item value="favorites" icon={<Heart />} ariaLabel="Favorites" />
</Tabs.List>
<Tabs.Panel value="preview">Preview the current document.</Tabs.Panel>
<Tabs.Panel value="favorites">Your favorite documents.</Tabs.Panel>
</Tabs>End Slots
On Tabs.Item, endSlot shows a count or badge after the label; keep it non-interactive. On Tabs.List, it pins an action to the end of a horizontal list, outside the scroll area.
import { Badge, Button, Tabs } from "@vortexlabs/vortex";
<Tabs defaultValue="all">
<Tabs.List endSlot={<Button size="sm" variant="secondary">New task</Button>}>
<Tabs.Item value="all" endSlot={<Badge size="sm" rounded>24</Badge>}>
All
</Tabs.Item>
<Tabs.Item value="open" endSlot={<Badge size="sm" rounded>3</Badge>}>
Open
</Tabs.Item>
</Tabs.List>
</Tabs>Disabled Items
Set disabled on an item to prevent selecting it; keyboard navigation skips it.
<Tabs defaultValue="overview">
<Tabs.List>
<Tabs.Item value="overview">Overview</Tabs.Item>
<Tabs.Item value="reports" disabled>Reports</Tabs.Item>
<Tabs.Item value="activity">Activity</Tabs.Item>
</Tabs.List>
</Tabs>Controlled Selection
The first enabled item is selected by default; set defaultValue when items render through custom components or load later. Pass value and update it in onChange. Inactive panels unmount their content, so keep state that must persist outside the panels.
"use client";
import { Button, Stack, Tabs } from "@vortexlabs/vortex";
import { useState } from "react";
export const TabsControlledExample = () => {
const [value, setValue] = useState("details");
return (
<Stack gap={4}>
<Tabs value={value} onChange={({ value }) => setValue(value)}>
<Tabs.List>
<Tabs.Item value="details">Details</Tabs.Item>
<Tabs.Item value="preview">Preview</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="details">Edit the project details.</Tabs.Panel>
<Tabs.Panel value="preview">Review the project before publishing.</Tabs.Panel>
</Tabs>
<Button variant="secondary" onClick={() => setValue("preview")}>
Open preview
</Button>
</Stack>
);
};Form Selection
Set name to render items as native radio inputs that submit the selected value with the form. Use this for a single choice, inside a fieldset with a legend and without panels.
"use client";
import { Button, Stack, Tabs, Text } from "@vortexlabs/vortex";
import { useState } from "react";
export const TabsFormExample = () => {
const [saved, setSaved] = useState<string | null>(null);
return (
<form
onSubmit={(event) => {
event.preventDefault();
setSaved(String(new FormData(event.currentTarget).get("billing")));
}}
>
<Stack gap={4}>
<fieldset>
<legend>Billing interval</legend>
<Tabs name="billing" variant="segment" defaultValue="monthly">
<Tabs.List>
<Tabs.Item value="monthly">Monthly</Tabs.Item>
<Tabs.Item value="yearly">Yearly</Tabs.Item>
</Tabs.List>
</Tabs>
</fieldset>
<Button attributes={{ type: "submit" }}>Save preference</Button>
{saved && <Text size="body-sm-desktop">Saved: {saved}</Text>}
</Stack>
</form>
);
};Accessibility
name, it selects them, like native radio buttons.ariaLabel for icon-only items.name in a fieldset with a legend.