Skip to main content

Tabs

Layered sections of content, called tab panels, that show one at a time.

asChild with Links

ColorsShadowsTypography

Account

Make changes to your account here. Click save when you're done.

Account

Make changes to your account here. Click save when you're done.

import { Card } from "@ngrok/mantle/card";import { Icon } from "@ngrok/mantle/icon";import { Tabs } from "@ngrok/mantle/tabs";import { GlobeIcon } from "@phosphor-icons/react/Globe";import { ShieldCheckIcon } from "@phosphor-icons/react/ShieldCheck";import { UserIcon } from "@phosphor-icons/react/User";import { Link, href } from "react-router"; <Tabs.Root orientation="horizontal" defaultValue="colors">	<Tabs.List>		<Tabs.Trigger value="colors" asChild>			<Link to={href("/base/colors")}>Colors</Link>		</Tabs.Trigger>		<Tabs.Trigger value="shadows" asChild>			<Link to={href("/base/shadows")}>Shadows</Link>		</Tabs.Trigger>		<Tabs.Trigger disabled value="tailwind-variants" asChild>			<Link to={href("/base/tailwind-variants")}>Tailwind Variants</Link>		</Tabs.Trigger>		<Tabs.Trigger value="typography" asChild>			<Link to={href("/base/typography")}>Typography</Link>		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root> <Tabs.Root orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-2">Tab Title</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			Tab Title		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">Tab Title</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root> <Tabs.Root orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>		<Tabs.Trigger value="tab-2">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root> <Tabs.Root orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-2">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root> <Tabs.Root orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-2">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root> <Tabs.Root orientation="horizontal" defaultValue="account" className="w-100 max-w-[400px]">	<Tabs.List>		<Tabs.Trigger value="account">			<Icon svg={<UserIcon />} />			Account			<Tabs.Badge>2</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="password">			<Icon svg={<ShieldCheckIcon />} />			Password		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator />	<Tabs.Content value="account">		<Card.Root>			<Card.Header>				<Card.Title>Account</Card.Title>				<p className="text-muted">					Make changes to your account here. Click save when you're done.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content>	<Tabs.Content value="password">		<Card.Root>			<Card.Header>				<Card.Title>Password</Card.Title>				<p className="text-muted">					Change your password here. After saving, you'll be logged out.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content></Tabs.Root> <Tabs.Root	orientation="vertical"	defaultValue="account"	className="scrollbar max-w-xl -m-1 p-1 overflow-x-scroll">	<Tabs.List>		<Tabs.Trigger value="account">Account</Tabs.Trigger>		<Tabs.Trigger value="password">Password</Tabs.Trigger>		<Tabs.Trigger value="disabled-tab" disabled>			Disabled tab		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator />	<Tabs.Content value="account">		<Card.Root>			<Card.Header>				<Card.Title>Account</Card.Title>				<p className="text-muted">					Make changes to your account here. Click save when you're done.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content>	<Tabs.Content value="password">		<Card.Root>			<Card.Header>				<Card.Title>Password</Card.Title>				<p className="text-muted">					Change your password here. After saving, you'll be logged out.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content></Tabs.Root>

Composition

Compose the parts of Tabs together to build your own:

Tabs.Root├── Tabs.List│   └── Tabs.Trigger│       └── Tabs.Badge├── Tabs.Separator└── Tabs.Content

Polymorphism

Tabs.Trigger accepts an asChild prop. When true, it composes the trigger styling and tab behavior onto its single child instead of rendering its own <button>. Use it to render each tab as a router link (a react-router Link here) so tabs drive client-side navigation while keeping the active-tab styling. A link tab activates on click, not on the press, so a press released off the link switches no tab and follows no link.

Tabs.Separator accepts asChild too. When true, it composes the hairline's styling and attributes onto your own element instead of rendering its own <div>.

Tabs.Root, Tabs.List, and Tabs.Content accept asChild through Radix. Each composes its part's attributes and behavior onto your own element instead of rendering its own <div>.

import { Tabs } from "@ngrok/mantle/tabs";import { Link, href } from "react-router"; <Tabs.Root orientation="horizontal" defaultValue="colors">	<Tabs.List>		<Tabs.Trigger value="colors" asChild>			<Link to={href("/base/colors")}>Colors</Link>		</Tabs.Trigger>		<Tabs.Trigger value="shadows" asChild>			<Link to={href("/base/shadows")}>Shadows</Link>		</Tabs.Trigger>		<Tabs.Trigger value="typography" asChild>			<Link to={href("/base/typography")}>Typography</Link>		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root>;

Horizontal overflow

When there are too many tabs to fit the container, the list scrolls horizontally with scroll-position-aware fade shadows at the edges.

import { Icon } from "@ngrok/mantle/icon";import { Tabs } from "@ngrok/mantle/tabs";import { ArrowsSplitIcon } from "@phosphor-icons/react/ArrowsSplit";import { BellIcon } from "@phosphor-icons/react/Bell";import { CertificateIcon } from "@phosphor-icons/react/Certificate";import { ChartBarIcon } from "@phosphor-icons/react/ChartBar";import { ChartLineIcon } from "@phosphor-icons/react/ChartLine";import { GearSixIcon } from "@phosphor-icons/react/GearSix";import { GlobeIcon } from "@phosphor-icons/react/Globe";import { KeyIcon } from "@phosphor-icons/react/Key";import { ListIcon } from "@phosphor-icons/react/List";import { LockIcon } from "@phosphor-icons/react/Lock";import { PlugIcon } from "@phosphor-icons/react/Plug";import { RocketLaunchIcon } from "@phosphor-icons/react/RocketLaunch";import { ShieldCheckIcon } from "@phosphor-icons/react/ShieldCheck";import { UserIcon } from "@phosphor-icons/react/User"; <Tabs.Root orientation="horizontal" defaultValue="getting-started" className="w-full">	<Tabs.List>		<Tabs.Trigger value="getting-started">			<Icon svg={<RocketLaunchIcon />} />			Getting Started		</Tabs.Trigger>		<Tabs.Trigger value="traffic-policy">			<Icon svg={<ArrowsSplitIcon />} />			Traffic Policy		</Tabs.Trigger>		<Tabs.Trigger value="api-keys">			<Icon svg={<KeyIcon />} />			API Keys		</Tabs.Trigger>		<Tabs.Trigger value="usage">			<Icon svg={<ChartBarIcon />} />			Usage		</Tabs.Trigger>		<Tabs.Trigger value="settings">			<Icon svg={<GearSixIcon />} />			Settings		</Tabs.Trigger>		<Tabs.Trigger value="logs">			<Icon svg={<ListIcon />} />			Logs		</Tabs.Trigger>		<Tabs.Trigger value="analytics">			<Icon svg={<ChartLineIcon />} />			Analytics		</Tabs.Trigger>		<Tabs.Trigger value="integrations">			<Icon svg={<PlugIcon />} />			Integrations		</Tabs.Trigger>		<Tabs.Trigger value="domains">			<Icon svg={<GlobeIcon />} />			Domains		</Tabs.Trigger>		<Tabs.Trigger value="team">			<Icon svg={<UserIcon />} />			Team		</Tabs.Trigger>		<Tabs.Trigger value="security">			<Icon svg={<ShieldCheckIcon />} />			Security		</Tabs.Trigger>		<Tabs.Trigger value="notifications">			<Icon svg={<BellIcon />} />			Notifications		</Tabs.Trigger>		<Tabs.Trigger value="certificates">			<Icon svg={<CertificateIcon />} />			Certificates		</Tabs.Trigger>		<Tabs.Trigger value="permissions">			<Icon svg={<LockIcon />} />			Permissions		</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root>;

Pill appearance

Use appearance="pill" to render each tab as a pill instead of the default classic underline style.

asChild with Links

ColorsShadowsTypography

Account

Make changes to your account here. Click save when you're done.

Account

Make changes to your account here. Click save when you're done.

import { Card } from "@ngrok/mantle/card";import { Icon } from "@ngrok/mantle/icon";import { Tabs } from "@ngrok/mantle/tabs";import { GlobeIcon } from "@phosphor-icons/react/Globe";import { ShieldCheckIcon } from "@phosphor-icons/react/ShieldCheck";import { UserIcon } from "@phosphor-icons/react/User";import { Link, href } from "react-router"; <Tabs.Root appearance="pill" orientation="horizontal" defaultValue="colors">	<Tabs.List>		<Tabs.Trigger value="colors" asChild>			<Link to={href("/base/colors")}>Colors</Link>		</Tabs.Trigger>		<Tabs.Trigger value="shadows" asChild>			<Link to={href("/base/shadows")}>Shadows</Link>		</Tabs.Trigger>		<Tabs.Trigger disabled value="tailwind-variants" asChild>			<Link to={href("/base/tailwind-variants")}>Tailwind Variants</Link>		</Tabs.Trigger>		<Tabs.Trigger value="typography" asChild>			<Link to={href("/base/typography")}>Typography</Link>		</Tabs.Trigger>	</Tabs.List></Tabs.Root> <Tabs.Root appearance="pill" orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-2">Tab Title</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			Tab Title		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">Tab Title</Tabs.Trigger>	</Tabs.List></Tabs.Root> <Tabs.Root appearance="pill" orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>		<Tabs.Trigger value="tab-2">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">			<Icon svg={<GlobeIcon />} />			Tab Title		</Tabs.Trigger>	</Tabs.List></Tabs.Root> <Tabs.Root appearance="pill" orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-2">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>	</Tabs.List></Tabs.Root> <Tabs.Root appearance="pill" orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-2">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger disabled value="tab-3">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="tab-4">			<Icon svg={<GlobeIcon />} />			Tab Title			<Tabs.Badge>32</Tabs.Badge>		</Tabs.Trigger>	</Tabs.List></Tabs.Root> <Tabs.Root	appearance="pill"	orientation="horizontal"	defaultValue="account"	className="w-100 max-w-[400px]">	<Tabs.List>		<Tabs.Trigger value="account">			<Icon svg={<UserIcon />} />			Account			<Tabs.Badge>2</Tabs.Badge>		</Tabs.Trigger>		<Tabs.Trigger value="password">			<Icon svg={<ShieldCheckIcon />} />			Password		</Tabs.Trigger>	</Tabs.List>	<Tabs.Content value="account">		<Card.Root>			<Card.Header>				<Card.Title>Account</Card.Title>				<p className="text-muted">					Make changes to your account here. Click save when you're done.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content>	<Tabs.Content value="password">		<Card.Root>			<Card.Header>				<Card.Title>Password</Card.Title>				<p className="text-muted">					Change your password here. After saving, you'll be logged out.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content></Tabs.Root> <Tabs.Root	appearance="pill"	orientation="vertical"	defaultValue="account"	className="scrollbar max-w-xl -m-1 p-1 overflow-x-scroll">	<Tabs.List>		<Tabs.Trigger value="account">Account</Tabs.Trigger>		<Tabs.Trigger value="password">Password</Tabs.Trigger>		<Tabs.Trigger value="disabled-tab" disabled>			Disabled tab		</Tabs.Trigger>	</Tabs.List>	<Tabs.Content value="account">		<Card.Root>			<Card.Header>				<Card.Title>Account</Card.Title>				<p className="text-muted">					Make changes to your account here. Click save when you're done.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content>	<Tabs.Content value="password">		<Card.Root>			<Card.Header>				<Card.Title>Password</Card.Title>				<p className="text-muted">					Change your password here. After saving, you'll be logged out.				</p>			</Card.Header>		</Card.Root>	</Tabs.Content></Tabs.Root>

Separator

Tabs.List draws no border of its own. Compose Tabs.Separator directly after it for the 1px hairline in the separator color: under the list when the root is horizontal, beside the list when the root is vertical. The separator is a sibling of the list and not a child, so it spans the root's full width and neither scrolls nor fades with the triggers.

As the root's direct child, the separator pulls itself up by --tabs-gap, the root's gap, and sits flush against the list while the content keeps the gap. Inside a wrapper of your own (a page header that holds a title, badges, and the list), the offset stays off and the separator sits where the wrapper lays it out. To change the space between the list and the content, set --tabs-gap on Tabs.Root. A gap-* class changes the gap alone and leaves the separator's offset at 1rem.

The separator is decorative (role="none"). Pass semantic to render role="separator".

with Tabs.Separator

without

vertical

The separator runs beside the list, the full height of the root.

import { Tabs } from "@ngrok/mantle/tabs"; <Tabs.Root orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-2">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-3">Tab Title</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator /></Tabs.Root> <Tabs.Root orientation="horizontal" defaultValue="tab-1">	<Tabs.List>		<Tabs.Trigger value="tab-1">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-2">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-3">Tab Title</Tabs.Trigger>	</Tabs.List></Tabs.Root> <Tabs.Root orientation="vertical" defaultValue="tab-1" className="max-w-xl">	<Tabs.List>		<Tabs.Trigger value="tab-1">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-2">Tab Title</Tabs.Trigger>		<Tabs.Trigger value="tab-3">Tab Title</Tabs.Trigger>	</Tabs.List>	<Tabs.Separator />	<Tabs.Content value="tab-1">		<p className="text-muted">The separator runs beside the list, the full height of the root.</p>	</Tabs.Content>	<Tabs.Content value="tab-2">		<p className="text-muted">Tab 2 content.</p>	</Tabs.Content>	<Tabs.Content value="tab-3">		<p className="text-muted">Tab 3 content.</p>	</Tabs.Content></Tabs.Root>

Accessibility

Tabs follows the WAI-ARIA tabs pattern through Radix: the list is a role="tablist", each trigger is a role="tab" with aria-selected and aria-controls, and each content panel is a role="tabpanel" labelled by its tab.

  • Tab moves focus into the list and lands on the active tab. Only the active tab is a Tab stop, so the next Tab leaves the list. This holds for asChild triggers too, so a list of router links is one Tab stop, not one per link.
  • ArrowLeft / ArrowRight (or ArrowUp / ArrowDown when vertical) move focus between tabs and activate the focused tab. Home and End jump to the first and last tab.
  • When a horizontal list overflows, keyboard focus scrolls the focused tab to the center of the visible area. A pointer click never scrolls the list, so the tab you press stays under the pointer.
  • A disabled trigger carries aria-disabled. With asChild, it also drops the child's href, so a disabled link cannot navigate.

Data attributes

Data AttributeValueDescription
data-slot"tabs" | "tabs-list" | "tabs-trigger" | "tabs-trigger-label" | "tabs-badge" | "tabs-separator separator" | "tabs-content"Names each part, plus the trigger's label wrapper. The separator carries Separator's own slot after its own.
data-orientation"horizontal" | "vertical"On the root, list, triggers, separator, and content. Radix sets it on all but the separator.
data-appearance"classic" | "pill"On the root. Descendant parts style against it.
data-separatorpresentPresence-only, on the separator. Set by the mantle Separator.
data-state"active" | "inactive"On each trigger and content panel. Set by Radix.
data-disabledpresent when disabledPresence-only, on a trigger. Set by Radix.

API Reference

The Tabs components accept the following props.

Tabs.Root

Layered sections of content, called tab panels, that show one at a time. The outermost part; it owns orientation and appearance. It stamps both as data-orientation and data-appearance for the parts below to style against. Based on Radix Tabs Root.

PropTypeDefaultDescription
defaultValue?stringThe value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs.
value?stringThe controlled value of the tab to activate. Should be used in conjunction with onValueChange.
onValueChange?(value: string) => voidEvent handler called when the value changes. Fires once per activation, although Radix activates a trigger on mousedown and again on the focus it moves.
orientation?"horizontal" | "vertical""horizontal"The orientation of the tabs.
appearance?"classic" | "pill""classic"The appearance of the tabs. Classic appearance shows the tab list with an underline; pill appearance shows each tab as a pill.
asChild?booleanfalseWhen true, renders the single child instead of its own <div> and forwards the root's attributes and behavior onto it.
className?stringAdditional CSS classes to apply to the tabs root element.

CSS variables

CSS VariableDefaultDescription
--tabs-gap1remThe space between the list and the content. Tabs.Separator pulls itself up by it, so set the variable here instead of a gap-* class.

Data attributes

Data AttributeValueDescription
data-slot"tabs"On the root element.
data-orientation"horizontal" | "vertical"The orientation prop. Set by Radix.
data-appearance"classic" | "pill"The appearance prop. Descendant parts style against it.

Tabs.List

Contains the triggers that are aligned along the edge of the active content. Based on Radix Tabs List.

The list draws no border of its own. Compose Tabs.Separator after it for the hairline between the triggers and the content.

PropTypeDefaultDescription
asChild?booleanfalseWhen true, renders the single child instead of its own <div> and forwards the tablist's attributes and behavior onto it.
className?stringAdditional CSS classes to apply to the tabs list element.

Data attributes

Data AttributeValueDescription
data-slot"tabs-list"On the tablist element.
data-orientation"horizontal" | "vertical"The root's orientation. Set by Radix.

Tabs.Separator

The hairline between the list and the content, in the separator color. Compose it directly after Tabs.List. It follows the root's orientation: under the list when horizontal, beside the list when vertical. It is a sibling of the list and not a child, so it spans the root's full width and neither scrolls nor fades with the triggers. As the root's direct child it pulls itself up by --tabs-gap and sits flush against the list. Inside a wrapper of your own, the offset stays off. Composes the mantle Separator.

PropTypeDefaultDescription
semantic?booleanfalseWhen true, renders role="separator" with its aria-orientation. When false, renders role="none", so the hairline is decorative.
asChild?booleanfalseWhen true, renders the single child instead of its own <div> and forwards the classes, data-* attributes, and ref onto it. The part takes children only with asChild.
className?stringAdditional CSS classes to apply to the separator element.

CSS variables

CSS VariableDefaultDescription
--tabs-gap1remRead, not owned. Tabs.Root sets it; the separator cancels it to sit flush against the list.

Data attributes

Data AttributeValueDescription
data-slot"tabs-separator separator"On the separator element, joined ahead of Separator's own slot.
data-orientation"horizontal" | "vertical"The root's orientation.
data-separatorpresentPresence-only. Set by the mantle Separator.

Tabs.Trigger

The button that activates its associated content. Based on Radix Tabs Trigger.

children render inside a <span data-slot="tabs-trigger-label">, on the plain path and the asChild path alike. The active-tab decoration is a permanent element sibling, so without the span a bare text label is never a lone child: a browser translation engine reparents that text node, and the removal React runs when the label changes shape or goes away throws. The span is display: contents, so the icon, the label, and Tabs.Badge stay flex items of the trigger and keep its gap. Browser translation covers the mechanism and the rules your own components need.

An icon you pass as a child is now a grandchild of the trigger, so a [&>svg] class of your own no longer reaches it. Match the part's own variant instead: [&>[data-slot=tabs-trigger-label]>svg]:size-4. A [&_svg] class or a class on the icon itself loses to the trigger's default, which carries one more attribute selector.

PropTypeDefaultDescription
valuestringA unique value that associates the trigger with a content.
asChild?booleanfalseUse the asChild prop to compose the trigger styling and functionality onto alternative element types or your own React components.
disabled?booleanfalseWhen true, prevents the user from interacting with the tab.
className?stringAdditional CSS classes to apply to the trigger element.

Data attributes

Data AttributeValueDescription
data-slot"tabs-trigger"On the trigger element.
data-slot"tabs-trigger-label"On the <span> wrapping children.
data-orientation"horizontal" | "vertical"The root's orientation. Set by Radix.
data-state"active" | "inactive"Whether the trigger's panel is shown. Set by Radix.
data-disabledpresent when disabledPresence-only. Set by Radix.

Tabs.Content

Contains the content associated with each trigger. Based on Radix Tabs Content.

PropTypeDefaultDescription
valuestringA unique value that associates the content with a trigger.
asChild?booleanfalseWhen true, renders the single child instead of its own <div> and forwards the panel's attributes and behavior onto it.
className?stringAdditional CSS classes to apply to the content element.

Data attributes

Data AttributeValueDescription
data-slot"tabs-content"On the panel element.
data-orientation"horizontal" | "vertical"The root's orientation. Set by Radix.
data-state"active" | "inactive"Whether the panel is shown. Set by Radix.

Tabs.Badge

A badge to render inside a tab trigger, typically a count or a status indicator.

PropTypeDefaultDescription
childrenReactNodeThe content to be rendered inside the badge.
className?stringAdditional CSS classes to apply to the badge element.

Data attributes

Data AttributeValueDescription
data-slot"tabs-badge"On the badge element.