HextaUI

Tabs

Passez d’une vue à l’autre avec un indicateur qui glisse vers l’onglet actif, en contrôle segmenté ou souligné, et des listes qui défilent en cas de débordement.

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

Ajoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.

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>

Un seul indicateur glisse jusqu'à l'onglet actif et se redimensionne pour l'épouser, tandis que le contenu change instantanément pour que les changements fréquents n'attendent jamais une animation. L'indicateur est placé avant l'hydratation, il n'apparaît donc jamais d'un coup au chargement.

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

Soulignement

variant="line" sur TabsList fait glisser un soulignement plutôt qu'une pastille.

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>
  )
}

Nombreux onglets

Quand les onglets ne tiennent pas, la liste défile latéralement, estompe les bords où il y a plus à voir, et amène l'onglet actif ou focalisé dans la vue.

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>
  )
}

Nombreux onglets, segmentés

Le style pill défile de la même façon, en gardant sa piste pendant que l'onglet choisi reste visible.

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>
  )
}

Icônes et compteurs

Placez des icônes et des badges dans les déclencheurs.

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>
  )
}

Vertical

orientation="vertical" empile les onglets ; le soulignement passe sur le côté.

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>
  )
}

Désactivé

Les onglets désactivés sont ignorés par le clavier.

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>
  )
}

Contrôlé

Passez value et onValueChange. Les changements venus de l'extérieur font aussi glisser l'indicateur.

"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>
  )
}

De droite à gauche

Les onglets, les flèches et l'indicateur suivent le sens de lecture.

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>
  )
}
ToucheAction
TabEntre dans la liste sur l'onglet actif, puis dans le panneau.
←→Se déplace entre les onglets, en bouclant aux extrémités. ↑ et ↓ en vertical.
HomeEndVa au premier ou au dernier onglet.
EnterSpaceOuvre l'onglet qui a le focus. Définissez activateOnFocus sur TabsList pour ouvrir les onglets au fil du focus.
  • La liste est un tablist ; chaque déclencheur est un tab qui contrôle son tabpanel.
  • Les panneaux sont focalisables, pour que les utilisateurs du clavier atteignent un contenu sans liens ni boutons.
  • Avec la réduction des animations, l'indicateur saute au lieu de glisser.
PropTypePar défaut
value
any–
defaultValue
any0
onValueChange
(value, details) => void–
orientation
"horizontal" | "vertical""horizontal"
AttributDescription
data-activation-direction"left", "right", "up" ou "down" : où se trouve le nouvel onglet par rapport à l'ancien.
PropTypePar défaut
variant
"default" | "line""default"
activateOnFocus
booleanfalse
loopFocus
booleantrue
AttributDescription
data-slot="tabs-indicator"La pastille ou le soulignement glissant.
data-scrolled-start / data-scrolled-endPrésent quand la liste peut défiler dans ce sens.
--active-tab-left / --active-tab-widthPosition et taille de l'onglet actif, aussi -top et -height.
PropTypePar défaut
value
any–
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<button>
AttributDescription
data-activeL'onglet ouvert.
data-disabledL'onglet est désactivé.
PropTypePar défaut
value
any–
keepMountedGarde le panneau dans le DOM tant qu'il est masqué.
booleanfalse

Utilisé dans les blocks

Des blocks qui s’appuient sur Tabs.