- Accordion
- Alert
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendarnew
- Card
- Carousel
- Checkbox
- Chip
- Collapse
- Dialog
- Drawer
- Footer
- Gallery
- Icon Button
- Image
- Input
- List
- Mapnew
- Menu
- Navbar
- Pagination
- Popover
- Progress
- Radio
- Rating
- Select
- Sidebar
- Skeletonnew
- Slider
- Software Purchase Card
- Sonner / Toastnew
- Speed Dial
- Spinner
- Stepper
- Switch
- Table
- Tabs
- Textarea
- Timeline
- Tooltip
- Typography
- Video
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsDemo() {
return (
<Tabs defaultValue="html" className="w-full">
<TabsList className="w-full">
<TabsTrigger className="w-full" value="html">
HTML
</TabsTrigger>
<TabsTrigger className="w-full" value="react">
React
</TabsTrigger>
<TabsTrigger className="w-full" value="vue">
Vue
</TabsTrigger>
<TabsTrigger className="w-full" value="angular">
Angular
</TabsTrigger>
<TabsTrigger className="w-full" value="svelte">
Svelte
</TabsTrigger>
</TabsList>
<TabsContent value="html" className="mt-4">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are like
offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="react" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="vue" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
<TabsContent value="angular" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="svelte" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
</Tabs>
)
}
pnpm dlx @creative-tim/ui@latest add tabs
Usage
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"<Tabs defaultValue="account">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">Account content</TabsContent>
<TabsContent value="password">Password content</TabsContent>
</Tabs>Examples
Tabs Default
A simple horizontal tabs component with multiple tab panels that provides a clean interface for organizing and switching between different content sections.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsDemo() {
return (
<Tabs defaultValue="html" className="w-full">
<TabsList className="w-full">
<TabsTrigger className="w-full" value="html">
HTML
</TabsTrigger>
<TabsTrigger className="w-full" value="react">
React
</TabsTrigger>
<TabsTrigger className="w-full" value="vue">
Vue
</TabsTrigger>
<TabsTrigger className="w-full" value="angular">
Angular
</TabsTrigger>
<TabsTrigger className="w-full" value="svelte">
Svelte
</TabsTrigger>
</TabsList>
<TabsContent value="html" className="mt-4">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are like
offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="react" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="vue" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
<TabsContent value="angular" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="svelte" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
</Tabs>
)
}
Tabs Underline
Tabs with an underline style indicator for the active tab, providing a minimalist design that emphasizes the selected tab with a bottom border accent.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsUnderline() {
return (
<Tabs defaultValue="html" className="w-full">
<TabsList className="border-border w-full rounded-none border-b bg-transparent p-0">
<TabsTrigger
className="data-[state=active]:border-primary w-full rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="html"
>
HTML
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:border-primary w-full rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="react"
>
React
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:border-primary w-full rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="vue"
>
Vue
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:border-primary w-full rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="angular"
>
Angular
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:border-primary w-full rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="svelte"
>
Svelte
</TabsTrigger>
</TabsList>
<TabsContent value="html" className="mt-4">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are like
offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="react" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="vue" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
<TabsContent value="angular" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="svelte" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
</Tabs>
)
}
Tabs Transparent
Tabs with a transparent background and subtle active state, creating a lightweight appearance that blends seamlessly with the surrounding content.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsTransparent() {
return (
<Tabs defaultValue="html" className="w-full">
<TabsList className="w-full bg-transparent">
<TabsTrigger
className="data-[state=active]:bg-primary/10 w-full data-[state=active]:shadow-none"
value="html"
>
HTML
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:bg-primary/10 w-full data-[state=active]:shadow-none"
value="react"
>
React
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:bg-primary/10 w-full data-[state=active]:shadow-none"
value="vue"
>
Vue
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:bg-primary/10 w-full data-[state=active]:shadow-none"
value="angular"
>
Angular
</TabsTrigger>
<TabsTrigger
className="data-[state=active]:bg-primary/10 w-full data-[state=active]:shadow-none"
value="svelte"
>
Svelte
</TabsTrigger>
</TabsList>
<TabsContent value="html" className="mt-4">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are like
offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="react" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="vue" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
<TabsContent value="angular" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="svelte" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
</Tabs>
)
}
Tabs with Icon
Tabs that include icons alongside text labels, enhancing visual recognition and making it easier for users to quickly identify and navigate between different sections.
import { LayoutDashboard, Settings, User } from "lucide-react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsWithIcon() {
return (
<Tabs defaultValue="dashboard" className="w-full">
<TabsList className="w-full">
<TabsTrigger className="w-full" value="dashboard">
<LayoutDashboard className="mr-2 h-4 w-4" />
Dashboard
</TabsTrigger>
<TabsTrigger className="w-full" value="profile">
<User className="mr-2 h-4 w-4" />
User Profile
</TabsTrigger>
<TabsTrigger className="w-full" value="settings">
<Settings className="mr-2 h-4 w-4" />
Settings
</TabsTrigger>
</TabsList>
<TabsContent value="dashboard" className="mt-4">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are like
offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="profile" className="mt-4">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="settings" className="mt-4">
We're not always in the position that we want to be at. We're
constantly growing. We're constantly making mistakes. We're
constantly trying to express ourselves and actualize our dreams.
</TabsContent>
</Tabs>
)
}
Tabs Vertical
Vertically oriented tabs with content displayed to the side, ideal for sidebar navigation or when horizontal space is limited and vertical space is available.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsVertical() {
return (
<Tabs
defaultValue="html"
orientation="vertical"
className="flex w-full flex-row gap-4"
>
<TabsList className="h-fit flex-col">
<TabsTrigger className="w-full" value="html">
HTML
</TabsTrigger>
<TabsTrigger className="w-full" value="react">
React
</TabsTrigger>
<TabsTrigger className="w-full" value="vue">
Vue
</TabsTrigger>
<TabsTrigger className="w-full" value="angular">
Angular
</TabsTrigger>
<TabsTrigger className="w-full" value="svelte">
Svelte
</TabsTrigger>
</TabsList>
<div className="flex-1">
<TabsContent value="html">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are
like offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="react">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="vue">
We're not always in the position that we want to be at.
We're constantly growing. We're constantly making mistakes.
We're constantly trying to express ourselves and actualize our
dreams.
</TabsContent>
<TabsContent value="angular">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="svelte">
We're not always in the position that we want to be at.
We're constantly growing. We're constantly making mistakes.
We're constantly trying to express ourselves and actualize our
dreams.
</TabsContent>
</div>
</Tabs>
)
}
Tabs Vertical with Icon
Vertically oriented tabs that include icons alongside text labels, combining the benefits of vertical layout with visual iconography for improved navigation clarity.
import { LayoutDashboard, Settings, User } from "lucide-react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export function TabsVerticalWithIcon() {
return (
<Tabs
defaultValue="dashboard"
orientation="vertical"
className="flex w-full flex-row gap-4"
>
<TabsList className="h-fit flex-col">
<TabsTrigger className="w-full justify-start" value="dashboard">
<LayoutDashboard className="mr-2 h-4 w-4" />
Dashboard
</TabsTrigger>
<TabsTrigger className="w-full justify-start" value="profile">
<User className="mr-2 h-4 w-4" />
User Profile
</TabsTrigger>
<TabsTrigger className="w-full justify-start" value="settings">
<Settings className="mr-2 h-4 w-4" />
Settings
</TabsTrigger>
</TabsList>
<div className="flex-1">
<TabsContent value="dashboard">
It really matters and then like it really doesn't matter. What
matters is the people who are sparked by it. And the people who are
like offended by it, it doesn't matter.
</TabsContent>
<TabsContent value="profile">
Because it's about motivating the doers. Because I'm here to
follow my dreams and inspire other people to follow their dreams, too.
</TabsContent>
<TabsContent value="settings">
We're not always in the position that we want to be at.
We're constantly growing. We're constantly making mistakes.
We're constantly trying to express ourselves and actualize our
dreams.
</TabsContent>
</div>
</Tabs>
)
}
Component API
The Tabs component is built on top of Radix UI Tabs and supports all of its props.
Tabs
The root container for the tabs component.
- defaultValue:
string- The value of the tab that should be active when initially rendered. - value:
string- The controlled value of the tab that should be active. Use in conjunction withonValueChange. - onValueChange:
(value: string) => void- Event handler called when the value changes. - orientation:
"horizontal" | "vertical"- The orientation of the tabs (default:"horizontal"). - dir:
"ltr" | "rtl"- The reading direction of the tabs. - activationMode:
"automatic" | "manual"- Whether a tab is activated automatically on focus or manually on click.
TabsList
Container for the tab triggers.
- loop:
boolean- Whentrue, keyboard navigation will loop from last tab to first, and vice versa.
TabsTrigger
Individual tab button that activates its associated content panel.
- value:
string(required) - A unique value that associates the trigger with content. - disabled:
boolean- Whentrue, prevents the user from interacting with the tab.
TabsContent
Content panel associated with a tab trigger.
- value:
string(required) - A unique value that associates the content with a trigger. - forceMount:
boolean- Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.
Accessibility
The Tabs component follows the WAI-ARIA Tabs Pattern:
- Uses proper ARIA roles (
tablist,tab,tabpanel) - Supports keyboard navigation (Arrow keys, Home, End)
- Automatically manages focus
- Supports both automatic and manual activation modes
- Properly associates tabs with their content panels
Best Practices
- Use clear, concise labels for each tab
- Keep the number of tabs to a reasonable amount (typically 5-7 maximum)
- Consider using icons to help users quickly identify tab content
- For vertical tabs, ensure adequate width for tab labels
- Make sure tab content is lazy-loaded if it contains heavy resources
- Maintain consistent content structure across all tab panels