Skip to main content

Tabs

Tabs are widgets that let people switch between related views, such as a project's overview, activity, and settings.

Import

import { Tabs } from '@vortexlabs/vortex';
import type { TabsProps } from '@vortexlabs/vortex';

Allied components

Other links

Project summary and current status.
<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, startIcon, or endSlot.
  • Supports disabled items and controlled selection.
  • Works as a form field with name, using native radio inputs.
Reference Links
WAI-ARIA tabs pattern

Anatomy

<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>
Component Part
Description
Holds the selected value and shared styling.
Holds the tabs and scrolls them when they overflow.
Selects the panel with the same value.
Shows its content while its tab is selected.

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. The default is neutral.

<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.

Projects being prepared.
<Tabs variant="segment" equalWidthItems defaultValue="draft">
  <Stack gap={4}>
    <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>
  </Stack>
</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.

Update your name and profile details.
<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 startIcon to show an icon before the label. Icon-only items need ariaLabel instead of children.

Preview the current document.
import { Eye, Heart } from "@vortexlabs/vortex-icons";

<Tabs variant="pill" defaultValue="preview">
  <Stack gap={4}>
    <Tabs.List>
      <Tabs.Item value="preview" startIcon={<Eye />}>Preview</Tabs.Item>
      <Tabs.Item value="favorites" startIcon={<Heart />} ariaLabel="Favorites" />
    </Tabs.List>
    <Tabs.Panel value="preview">Preview the current document.</Tabs.Panel>
    <Tabs.Panel value="favorites">Your favorite documents.</Tabs.Panel>
  </Stack>
</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.

Edit the project details.
"use client";

import { Button, Stack, Tabs } from "@vortexlabs/vortex";
import React from "react";

export const TabsControlledExample = () => {
  const [value, setValue] = React.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.

Billing interval
"use client";

import { Button, Stack, Tabs, Text } from "@vortexlabs/vortex";
import React from "react";

export const TabsFormExample = () => {
  const [saved, setSaved] = React.useState<string | null>(null);

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        setSaved(String(new FormData(event.currentTarget).get("billing")));
      }}
    >
      <Stack gap={4}>
        <fieldset>
          <Stack as="legend" marginBottom={2}>Billing interval</Stack>
          <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

Key
Description
EnterSpace
Selects the focused tab and shows its panel.
Left arrowRight arrowUp arrowDown arrow
Moves focus between tabs without selecting them. With name, it selects them, like native radio buttons.
HomeEnd
Moves focus to the first or last tab.
Provide ariaLabel for icon-only items.
Wrap tabs that use name in a fieldset with a legend.