HextaUI

Tabs

Wechsle zwischen Ansichten mit einem Indikator, der zum aktiven Tab gleitet, als Segmented Control oder Unterstrich, und Listen, die bei Überlauf scrollen.

Overview

Visitors are up 12% this week, mostly from search.

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

export function TabsDemo() {
  return (
    <Tabs defaultValue="overview" className="w-full max-w-md">
      <TabsList>
        <TabsTrigger value="overview">Overview</TabsTrigger>
        <TabsTrigger value="analytics">Analytics</TabsTrigger>
        <TabsTrigger value="reports">Reports</TabsTrigger>
      </TabsList>
      <TabsContent value="overview">
        <div className="rounded-xl border p-4 text-sm">
          <p className="font-medium">Overview</p>
          <p className="text-muted-foreground">
            Visitors are up 12% this week, mostly from search.
          </p>
        </div>
      </TabsContent>
      <TabsContent value="analytics">
        <div className="rounded-xl border p-4 text-sm">
          <p className="font-medium">Analytics</p>
          <p className="text-muted-foreground">
            Average session length is 4m 12s across 18,240 visits.
          </p>
        </div>
      </TabsContent>
      <TabsContent value="reports">
        <div className="rounded-xl border p-4 text-sm">
          <p className="font-medium">Reports</p>
          <p className="text-muted-foreground">
            Three scheduled reports go out on Monday.
          </p>
        </div>
      </TabsContent>
    </Tabs>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/tabs.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
<Tabs defaultValue="overview">
  <TabsList>
    <TabsTrigger value="overview">Overview</TabsTrigger>
    <TabsTrigger value="analytics">Analytics</TabsTrigger>
  </TabsList>
  <TabsContent value="overview">…</TabsContent>
  <TabsContent value="analytics">…</TabsContent>
</Tabs>

Ein Indikator gleitet zum aktiven Tab und passt sich ihm in der Größe an, während der Inhalt sofort wechselt, sodass häufiges Wechseln nie auf eine Animation wartet. Der Indikator wird vor der Hydration platziert und springt daher beim Laden nie herein.

Tabs
├── TabsList
│   └── TabsTrigger
└── TabsContent

Unterstreichung

variant="line" an TabsList lässt eine Unterstreichung statt einer Pille gleiten.

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

const tabs = ["Activity", "Pull requests", "Issues", "Settings"]

export function TabsLine() {
  return (
    <Tabs defaultValue="Activity" className="w-full max-w-md">
      <TabsList variant="line">
        {tabs.map((tab) => (
          <TabsTrigger key={tab} value={tab}>
            {tab}
          </TabsTrigger>
        ))}
      </TabsList>
      {tabs.map((tab) => (
        <TabsContent key={tab} value={tab}>
          <p className="py-2 text-sm text-muted-foreground">
            {tab} for this repository.
          </p>
        </TabsContent>
      ))}
    </Tabs>
  )
}

Viele Tabs

Passen die Tabs nicht, scrollt die Liste seitlich, blendet die Ränder mit mehr Inhalt aus und scrollt den aktiven oder fokussierten Tab in den Blick.

import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

const months = [
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December",
]

export function TabsOverflow() {
  return (
    <Tabs defaultValue="September" className="w-full max-w-sm">
      <TabsList variant="line">
        {months.map((month) => (
          <TabsTrigger key={month} value={month}>
            {month}
          </TabsTrigger>
        ))}
      </TabsList>
    </Tabs>
  )
}

Viele Tabs, segmentiert

Der Pill-Stil scrollt genauso und behält seine Spur, während der gewählte Tab im Blick bleibt.

import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

const categories = [
  "All",
  "Design",
  "Engineering",
  "Marketing",
  "Sales",
  "Support",
  "Finance",
  "Legal",
  "Operations",
]

export function TabsOverflowSegmented() {
  return (
    <Tabs defaultValue="Finance" className="w-full max-w-xs">
      <TabsList>
        {categories.map((category) => (
          <TabsTrigger key={category} value={category}>
            {category}
          </TabsTrigger>
        ))}
      </TabsList>
    </Tabs>
  )
}

Icons und Zähler

Setze Icons und Badges in Trigger.

import { IconCode, IconGitPullRequest, IconMessage } from "@tabler/icons-react"

import { Badge } from "@/components/ui/badge"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

export function TabsIcons() {
  return (
    <Tabs defaultValue="code">
      <TabsList variant="line">
        <TabsTrigger value="code">
          <IconCode />
          Code
        </TabsTrigger>
        <TabsTrigger value="pulls">
          <IconGitPullRequest />
          Pull requests
          <Badge>12</Badge>
        </TabsTrigger>
        <TabsTrigger value="discussions">
          <IconMessage />
          Discussions
        </TabsTrigger>
      </TabsList>
    </Tabs>
  )
}

Vertikal

orientation="vertical" stapelt die Tabs; die Unterstreichung wandert zur Seite.

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

const sections = [
  { value: "general", label: "General", text: "Name, avatar and language." },
  { value: "security", label: "Security", text: "Password and two-factor." },
  { value: "billing", label: "Billing", text: "Plan, invoices and payment." },
]

export function TabsVertical() {
  return (
    <Tabs
      defaultValue="general"
      orientation="vertical"
      className="w-full max-w-md"
    >
      <TabsList variant="line">
        {sections.map((section) => (
          <TabsTrigger key={section.value} value={section.value}>
            {section.label}
          </TabsTrigger>
        ))}
      </TabsList>
      {sections.map((section) => (
        <TabsContent key={section.value} value={section.value}>
          <div className="flex flex-col gap-1 py-2 text-sm">
            <p className="font-medium">{section.label}</p>
            <p className="text-muted-foreground">{section.text}</p>
          </div>
        </TabsContent>
      ))}
    </Tabs>
  )
}

Deaktiviert

Deaktivierte Tabs werden von der Tastatur übersprungen.

import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

export function TabsDisabled() {
  return (
    <Tabs defaultValue="day">
      <TabsList>
        <TabsTrigger value="day">Day</TabsTrigger>
        <TabsTrigger value="week">Week</TabsTrigger>
        <TabsTrigger value="month" disabled>
          Month
        </TabsTrigger>
      </TabsList>
    </Tabs>
  )
}

Kontrolliert

Übergib value und onValueChange. Änderungen von außen lassen den Indikator ebenfalls gleiten.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

const steps = ["Cart", "Shipping", "Payment", "Review"]

export function TabsControlled() {
  const [step, setStep] = React.useState("Cart")
  const index = steps.indexOf(step)

  return (
    <div className="flex flex-col items-start gap-4">
      <Tabs value={step} onValueChange={(value) => setStep(String(value))}>
        <TabsList>
          {steps.map((item) => (
            <TabsTrigger key={item} value={item}>
              {item}
            </TabsTrigger>
          ))}
        </TabsList>
      </Tabs>
      <Button
        size="sm"
        variant="outline"
        onClick={() => setStep(steps[(index + 1) % steps.length])}
      >
        Next step
      </Button>
    </div>
  )
}

Rechts nach links

Tabs, Pfeiltasten und der Indikator folgen der Leserichtung.

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

export function TabsRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <Tabs defaultValue="account">
        <TabsList variant="line">
          <TabsTrigger value="account">الحساب</TabsTrigger>
          <TabsTrigger value="password">كلمة المرور</TabsTrigger>
          <TabsTrigger value="alerts">التنبيهات</TabsTrigger>
        </TabsList>
        <TabsContent value="account">
          <p className="py-2 text-sm text-muted-foreground">إعدادات الحساب.</p>
        </TabsContent>
        <TabsContent value="password">
          <p className="py-2 text-sm text-muted-foreground">
            غيّر كلمة المرور.
          </p>
        </TabsContent>
        <TabsContent value="alerts">
          <p className="py-2 text-sm text-muted-foreground">إدارة التنبيهات.</p>
        </TabsContent>
      </Tabs>
    </div>
  )
}
TasteAktion
TabWechselt in die Liste zum aktiven Tab, dann in das Panel.
←→Wechselt zwischen Tabs, an den Enden umlaufend. ↑ und ↓ bei vertikal.
HomeEndSpringt zum ersten oder letzten Tab.
EnterSpaceÖffnet den fokussierten Tab. Setze activateOnFocus an TabsList, um Tabs zu öffnen, während der Fokus wandert.
  • Die Liste ist eine tablist; jeder Trigger ist ein tab, der sein tabpanel steuert.
  • Panels sind fokussierbar, sodass Tastaturnutzer Inhalt ohne Links oder Buttons erreichen.
  • Bei reduzierter Bewegung springt der Indikator, statt zu gleiten.
PropTypStandard
value
any–
defaultValue
any0
onValueChange
(value, details) => void–
orientation
"horizontal" | "vertical""horizontal"
AttributBeschreibung
data-activation-direction"left", "right", "up" oder "down": wo der neue Tab relativ zum alten liegt.
PropTypStandard
variant
"default" | "line""default"
activateOnFocus
booleanfalse
loopFocus
booleantrue
AttributBeschreibung
data-slot="tabs-indicator"Die gleitende Pille oder Unterstreichung.
data-scrolled-start / data-scrolled-endVorhanden, wenn die Liste in diese Richtung scrollen kann.
--active-tab-left / --active-tab-widthPosition und Größe des aktiven Tabs, auch -top und -height.
PropTypStandard
value
any–
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<button>
AttributBeschreibung
data-activeDer geöffnete Tab.
data-disabledDer Tab ist deaktiviert.
PropTypStandard
value
any–
keepMountedHalte das Panel im DOM, während es verborgen ist.
booleanfalse

In Blocks verwendet

Blocks, die auf Tabs aufbauen.