shadcn.io is not affiliated with official shadcn/ui
Shadcn Tabs for React and Tailwind
Mutually exclusive panels in one region.
Profile
Manage your profile information
View you shared information here, update you profile name, picture and email.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/tabs.jsonpnpm dlx shadcn@latest add https://kit.dev/r/tabs.jsonnpx shadcn@latest add https://kit.dev/r/tabs.jsonyarn shadcn@latest add https://kit.dev/r/tabs.jsonInstall the following dependencies:
bun add @ark-ui/react tailwind-variantspnpm add @ark-ui/react tailwind-variantsnpm install @ark-ui/react tailwind-variantsyarn add @ark-ui/react tailwind-variantsCopy and paste the following code into your project.
"use client";
import {
Tabs as ArkTabs,
useTabs as useArkTabs,
useTabsContext as useArkTabsContext,
} from "@ark-ui/react/tabs";
import type React from "react";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
export const useTabs = useArkTabs;
export const useTabsContext = useArkTabsContext;
export const TabsContext = ArkTabs.Context;
const tabsRootClassName = cn(
"flex flex-col gap-2",
"data-[orientation=vertical]:flex-row"
);
export const Tabs = (props: React.ComponentProps<typeof ArkTabs.Root>) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ArkTabs.Root
className={cn(tabsRootClassName, className)}
data-slot="tabs"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const TabsRootProvider = (
props: React.ComponentProps<typeof ArkTabs.RootProvider>
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ArkTabs.RootProvider
className={cn(tabsRootClassName, className)}
data-slot="tabs"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
const tabsListVariants = tv({
defaultVariants: {
variant: "default",
},
slots: {
base: [
"relative z-0",
"w-fit",
"text-muted-foreground",
"flex items-center justify-center gap-x-0.5",
"data-[orientation=vertical]:flex-col",
],
indicator: [
"absolute inset-s-0 bottom-0",
"h-(--height) w-(--width)",
// zag-js drives the transition inline; these vars are its contract.
"[--transition-duration:200ms] [--transition-timing-function:ease-in-out]",
"motion-reduce:[--transition-duration:0ms]",
],
},
variants: {
variant: {
default: {
base: ["rounded-lg"],
indicator: ["-z-1 rounded-lg bg-accent"],
},
underline: {
base: [
"data-[orientation=vertical]:px-1",
"data-[orientation=horizontal]:py-1",
"*:data-[slot=tabs-tab]:hover:bg-accent",
],
indicator: [
"z-10",
"absolute bottom-0",
"bg-primary",
"data-[orientation=horizontal]:h-0.5",
"data-[orientation=vertical]:w-0.5",
],
},
},
},
});
interface TabsListProps
extends React.ComponentProps<typeof ArkTabs.List>,
VariantProps<typeof tabsListVariants> {}
export const TabsList = (props: TabsListProps) => {
const { variant = "default", className, children, ...rest } = props;
const { base, indicator } = tabsListVariants({ variant });
return (
<ArkTabs.List
className={cn(base(), className)}
data-slot="tabs-list"
{...rest}
>
{children}
{variant === "underline" ? (
<ArkTabs.Indicator
className={cn(indicator())}
data-slot="tab-indicator"
/>
) : null}
</ArkTabs.List>
);
};
export const TabsTrigger = (
props: React.ComponentProps<typeof ArkTabs.Trigger>
) => {
const { className, ...rest } = props;
return (
<ArkTabs.Trigger
className={cn(
"relative",
"h-9 sm:h-8",
"flex shrink-0 grow items-center justify-center gap-1.5",
"px-[calc(--spacing(2.5)-1px)]",
"whitespace-nowrap font-medium text-sm",
"rounded-lg border border-transparent",
"cursor-pointer",
"transition-[color,box-shadow]",
"data-[orientation=vertical]:w-full data-[orientation=vertical]:justify-start",
"hover:text-foreground/72",
"aria-selected:bg-accent aria-selected:text-foreground",
"data-selected:bg-accent data-selected:text-foreground",
"outline-none focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-ring/32",
"data-disabled:pointer-events-none data-disabled:opacity-64",
"[&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0",
"motion-reduce:transition-none!",
className
)}
data-slot="tabs-trigger"
{...rest}
/>
);
};
export const TabsContent = (
props: React.ComponentProps<typeof ArkTabs.Content>
) => {
const { className, ...rest } = props;
return (
<ArkTabs.Content
className={cn("flex-1 outline-none", className)}
data-slot="tabs-content"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
Tabs
├── TabsList
│ └── TabsIndicator
├── TabsTrigger
└── TabsContentUsage
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/components/ui/tabs";<Tabs defaultValue="tab-1">
<TabsList>
<TabsTrigger value="tab-1">Profile</TabsTrigger>
<TabsTrigger value="tab-2">Settings</TabsTrigger>
<TabsTrigger value="tab-3">Security</TabsTrigger>
</TabsList>
<TabsContent value="tab-1">
<Card className="h-full w-sm">
<CardHeader
description="Manage your profile information"
title="Profile"
/>
<CardContent className="text-muted-foreground text-sm">
View you shared information here, update you profile name, picture and
email.
</CardContent>
</Card>
</TabsContent>
<TabsContent value="tab-2">
<Card className="h-full w-sm">
<CardHeader description="Update your preferences" title="Settings" />
<CardContent className="text-muted-foreground text-sm">
Customize how to handle notifications, update the theme and text
density.
</CardContent>
</Card>
</TabsContent>
<TabsContent value="tab-3">
<Card className="h-full w-sm">
<CardHeader
description="Protect your account and data"
title="Security"
/>
<CardContent className="text-muted-foreground text-sm">
Update your security settings here, change your password and more.
</CardContent>
</Card>
</TabsContent>
</Tabs>Accessibility
| Key | Description |
|---|---|
ArrowDown | Move to the next item when orientation is "vertical". |
ArrowUp | Move to the previous item when orientation is "vertical". |
ArrowLeft | Move to the previous item when orientation is "horizontal". |
ArrowRight | Move to the next item when orientation is "horizontal". |
Home | Go to the start. |
End | Go to the end. |
Enter | Activate the focused control. |
Space | Activate the focused control. |
Demos
Context
Controlled
Disabled
Lazy Mount
Links
Manual
Orientation Horizontal
Orientation Vertical
Root Provider
Variant Default
Variant Underline
Variant Underline Orientation Vertical
With Icons
API Reference
Tabs
div
PropType
AttributeType
TabsList
PropType
AttributeType
TabsTrigger
PropType
AttributeType
TabsContent
PropType
AttributeType
TabsIndicator
AttributeType
CSS variableType
TabsRootProvider
div
PropType
AttributeType
CSS variableType
useTabs
PropType
TabsContext
PropType