Sidebar
Eine App-Seitenleiste, die zu Icons oder off-canvas einklappt, unter deinem Header fixiert bleibt und auf Smartphones zu einem wischbaren Sheet wird.
pnpm dlx shadcn@latest add https://hextaui.com/r/sidebar.jsonFügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.
Füge die Theme-Tokens zu deinem globalen CSS hinzu, falls du das noch nicht getan hast.
Installiere die Abhängigkeiten.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnKopiere den folgenden Code und füge ihn in dein Projekt ein.
components/ui/sidebar.tsx components/ui/button.tsx components/ui/input.tsx components/ui/sheet.tsx components/ui/skeleton.tsx components/ui/tooltip.tsx hooks/use-composed-ref.ts lib/hotkey.ts Passe die Importpfade an dein Projekt-Setup an.
Umschließe dein Layout mit <SidebarProvider /> und setze die Seite nach der Sidebar in <SidebarInset />.
SidebarProviderhält den Open-Zustand, das Tastenkürzel und die Breiten.Sidebarist eine Spalte, die beim Scrollen der Seite haften bleibt. Unter 768px wird sie zu einem Sheet.SidebarHeaderundSidebarFooterbleiben an Ort und Stelle.SidebarContentscrollt dazwischen.SidebarGroupist ein Abschnitt mit optionalem Label und optionaler Aktion.SidebarMenuhält die Links.SidebarInsetist die Seite neben der Sidebar.
Varianten
variant legt das Aussehen fest: eine volle sidebar, ein schwebendes floating-Panel oder inset, bei dem die Seite zu einer Karte auf der Sidebar-Farbe wird. Wechsle zwischen ihnen, um das Layout animiert zu sehen.
Collapsible
offcanvas gleitet die Sidebar aus dem Blick, icon schrumpft sie auf ihre Icons und zeigt jedes Label in einem Tooltip, und none hält sie offen. Drücke ⌘B oder Ctrl+B, um die Sidebar umzuschalten, in der du arbeitest.
Einklappbare Gruppen und Untermenüs
Umschließe eine SidebarGroup oder ein SidebarMenuItem mit einem Collapsible, rendere das Label oder den Button als Trigger und verschachtle ein SidebarMenuSub.
Rechte Seite
Setze side="right" und platziere die Sidebar nach SidebarInset. Rail und mobiles Sheet folgen der Seite.
Unter einem Header
Die Sidebar ist sticky und beginnt daher unter allem, was darüber liegt. Setze bei einem Sticky-Header --sidebar-top auf dessen Höhe, und die Sidebar haftet darunter und passt in den Rest des Bildschirms.
Kontrolliert
Übergib open und onOpenChange. Trigger, Rail und Tastenkürzel laufen alle über onOpenChange.
Lädt
SidebarMenuSkeleton füllt ein Menü, während es lädt. Die Breiten variieren pro Zeile und stimmen zwischen Server und Client überein.
Rechts nach links
Setze dir="rtl" und side="right". Abstände, Untermenü-Linien, Tooltips und das Trigger-Icon spiegeln sich.
Die Sidebar ist 16rem breit, auf Smartphones 18rem und eingeklappt auf Icons 3rem. Überschreibe --sidebar-width, --sidebar-width-mobile und --sidebar-width-icon am Provider.
Die Sidebar hat keine Seiteneffekte. Um zu merken, ob sie offen war, speichere es in onOpenChange und lies es beim Rendern auf dem Server in defaultOpen zurück.
| Taste | Aktion |
|---|---|
| ⌘ + BCtrl + B | Schaltet die Sidebar um. Bei mehreren Sidebars auf einer Seite reagiert die mit dem Fokus, sonst die erste. Wird ignoriert, während in einem Rich-Text-Editor getippt wird. |
| TabShift + Tab | Bewegt sich durch die Links. Eine eingeklappte Off-Canvas-Sidebar wird übersprungen. |
| EnterSpace | Aktiviert den fokussierten Link, Button oder Trigger. |
| Esc | Schließt die Sidebar auf Smartphones. |
SidebarTriggerträgt das Label „Toggle Sidebar“ und stelltaria-expandedundaria-controlsbereit.isActivesetztaria-current="page".- Beim Einklappen außerhalb des Canvas wird der Inhalt vor Tastatur und Screenreadern verborgen. Lag der Fokus darin, wandert er zum Trigger.
- Auf Icons eingeklappt, bleiben Labels im zugänglichen Namen jedes Links und erscheinen bei Hover und Tastaturfokus als Tooltip. Gruppenlabels, Aktionen und Badges sind ausgeblendet.
- Auf Smartphones ist die Sidebar ein modales Sheet: Der Fokus ist gefangen, Wischen oder Esc schließt sie, und das Folgen eines Links schließt sie. Links, die einen neuen Tab öffnen, Downloads und Klicks mit Modifier lassen sie offen.
- Bei aktivierter reduzierter Bewegung klappt sie sofort ein.
| Prop | Typ | Standard |
|---|---|---|
defaultOpen | boolean | true |
open | boolean | – |
onOpenChange | (open: boolean) => void | – |
keyboardShortcutmod ist ⌘ auf Apple-Plattformen und sonst Strg. null schaltet es ab. | string | null | "mod+b" |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-wrapper" | Der Layout-Wrapper. |
--sidebar-width | Breite im ausgeklappten Zustand. Standard ist 16rem. |
--sidebar-width-mobile | Breite des Phone-Sheets. Standard ist 18rem. |
--sidebar-width-icon | Breite im auf Icons eingeklappten Zustand. Standard ist 3rem. |
--sidebar-top | Wo die Sidebar beim Scrollen haftet, etwa unter einem Sticky-Header. Standard ist 0px. |
className und weitere Props gehen an den Sidebar-Container.
| Prop | Typ | Standard |
|---|---|---|
side | "left" | "right" | "left" |
variantplain entfernt Fläche, Randlinie, Scrollleiste und den Abstand zwischen Menü-Items, für eine Sidebar, die auf der Seite sitzt. | "sidebar" | "floating" | "inset" | "plain" | "sidebar" |
collapsible | "offcanvas" | "icon" | "none" | "offcanvas" |
mobileWie sie auf Smartphones öffnet: als Sheet von der Seite oder hereingleitend, um den Bildschirm zu füllen, wie in Chat-Apps. | "sheet" | "fullscreen" | "sheet" |
dirLegt auch die Richtung des Phone-Sheets fest. | "ltr" | "rtl" | – |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar" | Die Sidebar oder auf Smartphones das Sheet. |
data-state | "expanded" oder "collapsed". |
data-collapsible | Der Collapsible-Modus, solange eingeklappt, sonst "". Style Kinder mit group-data-[collapsible=icon]:. |
data-variant | Die Variante. |
data-side | Die Seite. |
data-mobile | Vorhanden am Phone-Sheet. |
data-slot="sidebar-container" | Das Panel, das gleitet und die Größe ändert. |
data-slot="sidebar-inner" | Die Fläche, die den Inhalt hält. |
Ein Ghost-Icon-Button, der toggleSidebar aufruft. Rufe event.preventDefault() in onClick auf, um das zu unterbinden. Übergib Kinder, um das Icon zu ersetzen.
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-trigger" | Der Trigger. |
aria-expanded | Ob die Sidebar offen ist. |
Eine schmale Trefferfläche am Rand der Sidebar, die sie per Klick umschaltet. Sie ist nicht in der Tab-Reihenfolge, da Trigger und Tastenkürzel Tastaturnutzer abdecken. Ist die Sidebar außerhalb des Canvas, bleibt die Rail am Bildschirmrand.
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-rail" | Die Rail. |
Ein <main>, das die restliche Breite einnimmt. Neben einer inset-Sidebar wird es zu einer abgerundeten Karte.
| Prop | Typ | Standard |
|---|---|---|
renderRendert ein anderes Element. Übergib ein <div />, wenn die Seite bereits ein <main>-Landmark hat. | React.ReactElement | (props) => React.ReactElement | – |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-inset" | Der Seitenbereich. |
Einfache <div>-Elemente. Der Inhalt scrollt mit einer dünnen Scrollleiste und weichen Rand-Fades.
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-header" | Oberer Abschnitt. |
data-slot="sidebar-content" | Scrollbarer Mittelteil. |
data-slot="sidebar-footer" | Unterer Abschnitt. |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-group" | Ein Abschnitt der Sidebar. |
data-slot="sidebar-group-content" | Der Inhalt der Gruppe. |
| Prop | Typ | Standard |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-group-label" | Gleitet nach oben und blendet aus, wenn auf Icons eingeklappt. |
| Prop | Typ | Standard |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-group-action" | Braucht ein zugängliches Label. |
Ein <ul> und seine <li>-Items. Setze gap="none" für dichte Listen wie Chatverläufe, in denen Zeilen bündig sitzen.
| Prop | Typ | Standard |
|---|---|---|
gapAbstand zwischen Items. Auch an SidebarMenuSub. | "default" | "none" | "default" |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-menu" | Die Liste. |
data-slot="sidebar-menu-item" | Ein Item. group/menu-item für Hover-Styles. |
| Prop | Typ | Standard |
|---|---|---|
isActiveHebt es hervor und setzt aria-current="page". | boolean | false |
variant | "default" | "outline" | "default" |
size | "default" | "sm" | "lg" | "default" |
tooltipWird angezeigt, solange sie auf Icons eingeklappt ist. Der Inhalt gleitet zwischen Items, während du dich am Menü entlang bewegst. | ReactNode | TooltipContentProps | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-menu-button" | peer/menu-button für Geschwister-Styles. |
data-active | Vorhanden, solange isActive. |
data-size | Die Größe. |
| Prop | Typ | Standard |
|---|---|---|
showOnHoverZeigt es nur, solange das Item gehovert oder fokussiert oder sein Menü offen ist. Auf Touchscreens immer sichtbar. | boolean | false |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-menu-action" | Braucht ein zugängliches Label. |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-menu-badge" | Eine Zahl am Ende des Items. |
| Prop | Typ | Standard |
|---|---|---|
showIcon | boolean | false |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-menu-skeleton" | Vor assistiver Technik verborgen. |
Eine verschachtelte Liste mit einer Linie am führenden Rand. Sie klappt weg, wenn die Sidebar auf Icons einklappt.
| Prop | Typ | Standard |
|---|---|---|
isActiveAn SidebarMenuSubButton. | boolean | false |
sizeAn SidebarMenuSubButton. | "sm" | "md" | "md" |
renderAn SidebarMenuSubButton. | ReactElement | (props, state) => ReactElement | <a> |
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-menu-sub" | Die verschachtelte Liste. |
data-slot="sidebar-menu-sub-button" | Ein verschachtelter Link. |
data-active | Vorhanden, solange isActive. |
Ein kleiner Input auf dem Seitenhintergrund und ein feiner Trenner.
| Attribut | Beschreibung |
|---|---|
data-slot="sidebar-input" | Das Input. |
data-slot="sidebar-separator" | Das Trennzeichen. |
Liest und steuert die nächste Sidebar. Wirft außerhalb von SidebarProvider.
| Prop | Typ | Standard |
|---|---|---|
state | "expanded" | "collapsed" | – |
open | boolean | – |
setOpen | (open: boolean) => void | – |
openMobile | boolean | – |
setOpenMobile | (open: boolean) => void | – |
isMobile | boolean | – |
toggleSidebarSchaltet auf Smartphones das Sheet um, sonst die Sidebar. | () => void | – |
- ButtonButtons in allen Varianten und Größen, mit eingebautem Lade-, Erfolgs- und Fehlerablauf, der den Spinner bei schnellen Anfragen überspringt.
- HotkeyTastenkürzel parsen, beschriften, ansagen und abgleichen, mit ⌘ auf Apple-Plattformen und Strg überall sonst.
- InputEin Texteingabefeld mit drei Größen, ungültigen und schreibgeschützten Zuständen, nativem Validierungsstyling und einer 16-px-Schrift für Touch, damit Smartphones nie hineinzoomen.
- SheetEin Panel, das von jeder Kante hereingleitet, mit Wischen zum Schließen, Scroll-Sperre und gestapelter Verschachtelung.
- SkeletonPlatzhalter, die 150 ms warten, bevor sie erscheinen, exakt die Größe des umschlossenen Inhalts annehmen und ihn einblenden, ohne etwas zu verschieben.
- TooltipEin kurzer Hinweis bei Hover oder Tastaturfokus, der sich nach kurzer Ruhezeit öffnet, zwischen benachbarten Elementen sofort wechselt und Tastenkürzel anzeigt.
In Blocks verwendet
Blocks, die auf Sidebar aufbauen.
- Chat SidebarDie Seitenleiste für eine Chat-App. Logo, Suche und Neuer Chat oben, deine eigenen Links darunter, angeheftete Chats, Projekte, die sich aufklappen und ihre Chats zeigen, zuletzt verwendete nach Tag gruppiert und Zeilen mit Hover- und Rechtsklick-Menüs, direktem Umbenennen, Löschen mit Rückgängig und Live-Antwortzuständen.
- HextaAIEine vollständige KI-Chat-App, gebaut aus allen KI-Blocks von HextaUI. Chats in einer Seitenleiste, Denkprozess mit Quellen, Tool-Aufrufe mit Diffs und Freigaben, ein Plan, den du prüfst, bevor der Agent läuft, gestreamtes Markdown und gestreamter Code sowie ein stiller Sprachmodus, alles gesteuert über AI-SDK-Message-Parts.