Creative Tim UICreative Tim UI

Shadcn Tabs

PreviousNext

A set of layered sections of content—known as tab panels—that are displayed one at a time.

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.
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&apos;t matter. What
        matters is the people who are sparked by it. And the people who are like
        offended by it, it doesn&apos;t matter.
      </TabsContent>
      <TabsContent value="react" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="vue" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;re
        constantly trying to express ourselves and actualize our dreams.
      </TabsContent>
      <TabsContent value="angular" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="svelte" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;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.

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.
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&apos;t matter. What
        matters is the people who are sparked by it. And the people who are like
        offended by it, it doesn&apos;t matter.
      </TabsContent>
      <TabsContent value="react" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="vue" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;re
        constantly trying to express ourselves and actualize our dreams.
      </TabsContent>
      <TabsContent value="angular" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="svelte" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;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.

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.
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&apos;t matter. What
        matters is the people who are sparked by it. And the people who are like
        offended by it, it doesn&apos;t matter.
      </TabsContent>
      <TabsContent value="react" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="vue" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;re
        constantly trying to express ourselves and actualize our dreams.
      </TabsContent>
      <TabsContent value="angular" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="svelte" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;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.

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.
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&apos;t matter. What
        matters is the people who are sparked by it. And the people who are like
        offended by it, it doesn&apos;t matter.
      </TabsContent>
      <TabsContent value="react" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="vue" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;re
        constantly trying to express ourselves and actualize our dreams.
      </TabsContent>
      <TabsContent value="angular" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="svelte" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;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.

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.
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&apos;t matter. What
        matters is the people who are sparked by it. And the people who are like
        offended by it, it doesn&apos;t matter.
      </TabsContent>
      <TabsContent value="profile" className="mt-4">
        Because it&apos;s about motivating the doers. Because I&apos;m here to
        follow my dreams and inspire other people to follow their dreams, too.
      </TabsContent>
      <TabsContent value="settings" className="mt-4">
        We&apos;re not always in the position that we want to be at. We&apos;re
        constantly growing. We&apos;re constantly making mistakes. We&apos;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.

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.
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&apos;t matter. What
          matters is the people who are sparked by it. And the people who are
          like offended by it, it doesn&apos;t matter.
        </TabsContent>
        <TabsContent value="react">
          Because it&apos;s about motivating the doers. Because I&apos;m here to
          follow my dreams and inspire other people to follow their dreams, too.
        </TabsContent>
        <TabsContent value="vue">
          We&apos;re not always in the position that we want to be at.
          We&apos;re constantly growing. We&apos;re constantly making mistakes.
          We&apos;re constantly trying to express ourselves and actualize our
          dreams.
        </TabsContent>
        <TabsContent value="angular">
          Because it&apos;s about motivating the doers. Because I&apos;m here to
          follow my dreams and inspire other people to follow their dreams, too.
        </TabsContent>
        <TabsContent value="svelte">
          We&apos;re not always in the position that we want to be at.
          We&apos;re constantly growing. We&apos;re constantly making mistakes.
          We&apos;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.

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.
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&apos;t matter. What
          matters is the people who are sparked by it. And the people who are
          like offended by it, it doesn&apos;t matter.
        </TabsContent>
        <TabsContent value="profile">
          Because it&apos;s about motivating the doers. Because I&apos;m here to
          follow my dreams and inspire other people to follow their dreams, too.
        </TabsContent>
        <TabsContent value="settings">
          We&apos;re not always in the position that we want to be at.
          We&apos;re constantly growing. We&apos;re constantly making mistakes.
          We&apos;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 with onValueChange.
  • 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 - When true, 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 - When true, 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