Skip to main content
PolyUI/docs

Navigation

Tabs

Switch between multiple content panels. Supports keyboard navigation (arrow keys) and ARIA tablist semantics.

Installation

bash
pnpm dlx shadcn@latest add tabs -c packages/react

Default

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsDefault() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore">
        <TabsList>
          {TABS.map((tab) => (
            <TabsTrigger key={tab.value} value={tab.value}>
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

With Icons

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsWithIcons() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList>
          {TABS.map(({ icon: Icon, name, value }) => (
            <TabsTrigger key={value} value={value} className="flex items-center gap-1 px-2.5 sm:px-3">
              <Icon />
              {name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

With Badge

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
import { Badge } from "@polyui/react/badge"
export function TabsWithBadge() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList>
          {TABS.map((tab) => (
            <TabsTrigger key={tab.value} value={tab.value} className="flex items-center gap-1 px-2.5 sm:px-3">
              {tab.name}
              <Badge className="h-5 min-w-5 px-1 tabular-nums">{tab.count}</Badge>
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Solid Pills

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsSolidPills() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="data-active:bg-primary dark:data-active:bg-primary data-active:text-primary-foreground dark:data-active:text-primary-foreground dark:data-active:border-transparent"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Outlined Pills

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsOutlinedPills() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="data-active:border-border data-active:shadow-none"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Underline

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsUnderline() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background rounded-none border-b p-0">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="bg-background data-active:border-primary dark:data-active:border-primary h-full rounded-none border-0 border-b-2 border-transparent data-active:shadow-none"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Sharp

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsSharp() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background rounded-none border-b p-0">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="bg-background data-active:border-primary dark:data-active:border-primary h-full rounded-none border-b-2 border-transparent data-active:shadow-none"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Lifted

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsLifted() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background justify-start rounded-none border-b p-0">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="bg-background border-b-border dark:data-active:bg-background data-active:border-border data-active:border-b-background h-full rounded-none rounded-t border border-transparent data-active:-mb-0.5 data-active:shadow-none dark:border-b-0 dark:data-active:-mb-0.5"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Custom

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsCustom() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background gap-1">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="data-active:bg-primary dark:data-active:bg-primary data-active:text-primary-foreground dark:data-active:text-primary-foreground text-muted-foreground hover:text-foreground hover:bg-muted transition-colors duration-300 hover:border dark:data-active:border-transparent"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Custom Underline

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsCustomUnderline() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" className="gap-4">
        <TabsList className="bg-background rounded-none border-b p-0">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="bg-background data-active:border-primary dark:data-active:border-primary data-active:text-foreground text-muted-foreground dark:text-muted-foreground hover:text-foreground dark:hover:text-foreground hover:border-muted-foreground/30 h-full rounded-none border-0 border-b-2 border-transparent data-active:shadow-none"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Vertical

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsVertical() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" orientation="vertical">
        <TabsList className="h-full">
          {TABS.map((tab) => (
            <TabsTrigger key={tab.value} value={tab.value} className="w-full">
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Vertical Underline

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsVerticalUnderline() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" orientation="vertical">
        <TabsList className="bg-background h-full rounded-none border-l p-0">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="bg-background data-active:border-primary dark:data-active:border-primary h-full w-full justify-start rounded-none border-0 border-l-2 border-transparent data-active:shadow-none"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Vertical Soft

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsVerticalSoft() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" orientation="vertical">
        <TabsList className="bg-background h-full">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="data-active:bg-primary/20 data-active:text-primary dark:data-active:text-primary dark:data-active:bg-primary/20 w-full data-active:shadow-none dark:data-active:border-transparent"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Vertical Solid

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsVerticalSolid() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" orientation="vertical">
        <TabsList className="bg-background h-full">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="data-active:bg-primary dark:data-active:bg-primary data-active:text-primary-foreground dark:data-active:text-primary-foreground w-full dark:data-active:border-transparent"
            >
              {tab.name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Vertical with Icons

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
export function TabsVerticalWithIcons() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" orientation="vertical">
        <TabsList className="h-full">
          {TABS.map(({ icon: Icon, name, value }) => (
            <TabsTrigger
              key={value}
              value={value}
              className="flex w-full items-center justify-start gap-1.5 px-2.5 sm:px-3"
            >
              <Icon />
              {name}
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Vertical with Badge

Discover fresh ideas, trending topics, and hidden gems curated just for you. Start exploring and let your curiosity lead the way!

tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@polyui/react/tabs"
import { Badge } from "@polyui/react/badge"
export function TabsVerticalWithBadge() {
  return (
    <div className="w-full max-w-md">
      <Tabs defaultValue="explore" orientation="vertical">
        <TabsList className="h-full gap-1.5">
          {TABS.map((tab) => (
            <TabsTrigger
              key={tab.value}
              value={tab.value}
              className="flex w-full items-center justify-start gap-1.5 px-2.5 sm:px-3"
            >
              {tab.name}
              <Badge className="h-5 min-w-5 px-1 tabular-nums">{tab.count}</Badge>
            </TabsTrigger>
          ))}
        </TabsList>
        {TABS.map((tab) => (
          <TabsContent key={tab.value} value={tab.value}>
            <p className="text-muted-foreground text-sm">{tab.content}</p>
          </TabsContent>
        ))}
      </Tabs>
    </div>
  )
}

Props

Tabs

属性类型默认值说明
defaultValuestringUncontrolled default selected tab
valuestringControlled selected tab
onValueChange(value: string) => voidTab change callback
orientation"horizontal" | "vertical""horizontal"Layout orientation

TabsTrigger

属性类型默认值说明
value*stringCorresponding content value
disabledbooleanfalseDisable this tab

TabsContent

属性类型默认值说明
value*stringCorresponding trigger value
forceMountbooleanfalseAlways mount DOM (for SSR or animation)