Sidebar
Une barre latérale d’application qui se réduit en icônes ou hors canevas, reste épinglée sous votre en-tête et devient une sheet balayable sur mobile.
pnpm dlx shadcn@latest add https://hextaui.com/r/sidebar.jsonAjoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.
Ajoutez les tokens de thème à votre CSS global, si ce n’est pas déjà fait.
Installez les dépendances.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopiez et collez le code suivant dans votre projet.
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 Mettez à jour les chemins d’import selon la configuration de votre projet.
Entourez votre mise en page de <SidebarProvider /> et placez la page dans <SidebarInset />, après la sidebar.
SidebarProvidercontient l'état d'ouverture, le raccourci clavier et les largeurs.Sidebarest une colonne qui reste fixée pendant le défilement de la page. En dessous de 768px, elle devient une sheet.SidebarHeaderetSidebarFooterrestent en place.SidebarContentdéfile entre les deux.SidebarGroupest une section avec un label et une action facultatifs.SidebarMenucontient les liens.SidebarInsetest la page à côté de la sidebar.
Variantes
variant définit l'apparence : une sidebar pleine hauteur, un panneau floating, ou inset, où la page devient une carte sur la couleur de la sidebar. Passez de l'une à l'autre pour voir la mise en page s'animer.
Collapsible
offcanvas fait glisser la sidebar hors de la vue, icon la réduit à ses icônes et affiche chaque label dans un tooltip, et none la garde ouverte. Appuyez sur ⌘B ou Ctrl+B pour basculer la sidebar dans laquelle vous travaillez.
Groupes repliables et sous-menus
Entourez un SidebarGroup ou un SidebarMenuItem d'un Collapsible, rendez le label ou le bouton comme déclencheur, et imbriquez un SidebarMenuSub.
Côté droit
Définissez side="right" et placez la sidebar après SidebarInset. Le rail et la sheet mobile suivent le côté.
Sous un en-tête
La sidebar est sticky : elle démarre donc sous tout ce qui la précède. Avec un en-tête sticky, définissez --sidebar-top à sa hauteur et la sidebar se fixe en dessous et occupe le reste de l'écran.
Contrôlé
Passez open et onOpenChange. Le déclencheur, le rail et le raccourci passent tous par onOpenChange.
Chargement
SidebarMenuSkeleton remplit un menu pendant son chargement. Les largeurs varient selon la ligne et sont identiques entre serveur et client.
De droite à gauche
Définissez dir="rtl" et side="right". L'espacement, les traits des sous-menus, les tooltips et l'icône du déclencheur sont inversés.
La sidebar fait 16rem de large, 18rem sur téléphone et 3rem réduite aux icônes. Remplacez --sidebar-width, --sidebar-width-mobile et --sidebar-width-icon sur le provider.
La sidebar n'a aucun effet de bord. Pour mémoriser si elle était ouverte, enregistrez l'état dans onOpenChange et relisez-le dans defaultOpen lors du rendu côté serveur.
| Touche | Action |
|---|---|
| ⌘ + BCtrl + B | Bascule la sidebar. Avec plusieurs sidebars sur une page, celle qui a le focus répond, sinon la première. Ignoré pendant la saisie dans un éditeur de texte riche. |
| TabShift + Tab | Parcourt les liens. Une sidebar repliée hors canevas est ignorée. |
| EnterSpace | Active le lien, bouton ou déclencheur qui a le focus. |
| Esc | Ferme la sidebar sur téléphone. |
SidebarTriggera le label « Toggle Sidebar » et exposearia-expandedetaria-controls.isActivedéfinitaria-current="page".- Replier hors canevas masque le contenu au clavier et aux lecteurs d'écran. Si le focus était à l'intérieur, il passe au déclencheur.
- Réduite aux icônes, les labels restent dans le nom accessible de chaque lien et s'affichent en tooltip au survol et au focus clavier. Les labels de groupe, actions et badges sont masqués.
- Sur téléphone, la sidebar est une sheet modale : le focus est piégé, un balayage ou Esc la ferme, et suivre un lien la ferme. Les liens qui ouvrent un nouvel onglet, les téléchargements et les clics avec modificateur la laissent ouverte.
- Avec la réduction des animations, le repli est instantané.
| Prop | Type | Par défaut |
|---|---|---|
defaultOpen | boolean | true |
open | boolean | – |
onOpenChange | (open: boolean) => void | – |
keyboardShortcutmod est ⌘ sur les plateformes Apple et Ctrl ailleurs. null le désactive. | string | null | "mod+b" |
| Attribut | Description |
|---|---|
data-slot="sidebar-wrapper" | Le wrapper de mise en page. |
--sidebar-width | Largeur déployée. 16rem par défaut. |
--sidebar-width-mobile | Largeur de la sheet du téléphone. 18rem par défaut. |
--sidebar-width-icon | Largeur réduite aux icônes. 3rem par défaut. |
--sidebar-top | Où la sidebar se fixe pendant le défilement, par exemple sous un en-tête sticky. 0px par défaut. |
className et les autres props vont au conteneur de la sidebar.
| Prop | Type | Par défaut |
|---|---|---|
side | "left" | "right" | "left" |
variantplain supprime la surface, le trait de bord, la barre de défilement et l'espace entre les éléments du menu, pour une sidebar posée sur la page. | "sidebar" | "floating" | "inset" | "plain" | "sidebar" |
collapsible | "offcanvas" | "icon" | "none" | "offcanvas" |
mobileComment elle s'ouvre sur téléphone : une sheet latérale, ou un glissement qui remplit l'écran, comme les applications de chat. | "sheet" | "fullscreen" | "sheet" |
dirDéfinit aussi la direction de la sheet sur téléphone. | "ltr" | "rtl" | – |
| Attribut | Description |
|---|---|
data-slot="sidebar" | La sidebar, ou la sheet sur téléphone. |
data-state | "expanded" ou "collapsed". |
data-collapsible | Le mode repliable quand elle est repliée, sinon "". Stylez les enfants avec group-data-[collapsible=icon]:. |
data-variant | La variante. |
data-side | Le côté. |
data-mobile | Présent sur la sheet du téléphone. |
data-slot="sidebar-container" | Le panneau qui glisse et se redimensionne. |
data-slot="sidebar-inner" | La surface qui contient le contenu. |
Un Button icône ghost qui appelle toggleSidebar. Appelez event.preventDefault() dans onClick pour l'arrêter. Passez des enfants pour remplacer l'icône.
| Attribut | Description |
|---|---|
data-slot="sidebar-trigger" | Le déclencheur. |
aria-expanded | Indique si la sidebar est ouverte. |
Une fine zone de clic sur le bord de la sidebar qui la bascule au clic. Elle est exclue de l'ordre de tabulation car le déclencheur et le raccourci couvrent les utilisateurs du clavier. Quand la sidebar est hors canevas, le rail reste au bord de l'écran.
| Attribut | Description |
|---|---|
data-slot="sidebar-rail" | Le rail. |
Un <main> qui prend le reste de la largeur. À côté d'une sidebar inset, il devient une carte arrondie.
| Prop | Type | Par défaut |
|---|---|---|
renderRend un autre élément. Passez un <div /> quand la page a déjà un repère <main>. | React.ReactElement | (props) => React.ReactElement | – |
| Attribut | Description |
|---|---|
data-slot="sidebar-inset" | La zone de la page. |
Des éléments <div> simples. Le contenu défile avec une fine barre de défilement et de doux fondus de bord.
| Attribut | Description |
|---|---|
data-slot="sidebar-header" | Section du haut. |
data-slot="sidebar-content" | Milieu défilant. |
data-slot="sidebar-footer" | Section du bas. |
| Attribut | Description |
|---|---|
data-slot="sidebar-group" | Une section de la sidebar. |
data-slot="sidebar-group-content" | Le contenu du groupe. |
| Prop | Type | Par défaut |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Description |
|---|---|
data-slot="sidebar-group-label" | Glisse vers le haut et s'estompe quand elle est réduite aux icônes. |
| Prop | Type | Par défaut |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Description |
|---|---|
data-slot="sidebar-group-action" | Nécessite un label accessible. |
Un <ul> et ses éléments <li>. Définissez gap="none" pour des listes denses comme l'historique de chat, où les lignes se touchent.
| Prop | Type | Par défaut |
|---|---|---|
gapEspace entre les éléments. Aussi sur SidebarMenuSub. | "default" | "none" | "default" |
| Attribut | Description |
|---|---|
data-slot="sidebar-menu" | La liste. |
data-slot="sidebar-menu-item" | Un élément. group/menu-item pour les styles au survol. |
| Prop | Type | Par défaut |
|---|---|---|
isActiveLe met en évidence et définit aria-current="page". | boolean | false |
variant | "default" | "outline" | "default" |
size | "default" | "sm" | "lg" | "default" |
tooltipAffiché quand elle est réduite aux icônes. Le contenu glisse d'un élément à l'autre quand vous parcourez le menu. | ReactNode | TooltipContentProps | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Description |
|---|---|
data-slot="sidebar-menu-button" | peer/menu-button pour les styles de frères. |
data-active | Présent tant que isActive est vrai. |
data-size | La taille. |
| Prop | Type | Par défaut |
|---|---|---|
showOnHoverNe l'affiche que tant que l'élément est survolé ou focalisé, ou que son menu est ouvert. Toujours visible sur écran tactile. | boolean | false |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Description |
|---|---|
data-slot="sidebar-menu-action" | Nécessite un label accessible. |
| Attribut | Description |
|---|---|
data-slot="sidebar-menu-badge" | Un compteur à la fin de l'élément. |
| Prop | Type | Par défaut |
|---|---|---|
showIcon | boolean | false |
| Attribut | Description |
|---|---|
data-slot="sidebar-menu-skeleton" | Masqué aux technologies d'assistance. |
Une liste imbriquée avec un trait sur son bord de début. Elle se replie quand la sidebar se réduit aux icônes.
| Prop | Type | Par défaut |
|---|---|---|
isActiveSur SidebarMenuSubButton. | boolean | false |
sizeSur SidebarMenuSubButton. | "sm" | "md" | "md" |
renderSur SidebarMenuSubButton. | ReactElement | (props, state) => ReactElement | <a> |
| Attribut | Description |
|---|---|
data-slot="sidebar-menu-sub" | La liste imbriquée. |
data-slot="sidebar-menu-sub-button" | Un lien imbriqué. |
data-active | Présent tant que isActive est vrai. |
Un petit Input sur l'arrière-plan de la page, et un séparateur en filet.
| Attribut | Description |
|---|---|
data-slot="sidebar-input" | Le champ. |
data-slot="sidebar-separator" | Le séparateur. |
Lit et contrôle la sidebar la plus proche. Lève une erreur en dehors de SidebarProvider.
| Prop | Type | Par défaut |
|---|---|---|
state | "expanded" | "collapsed" | – |
open | boolean | – |
setOpen | (open: boolean) => void | – |
openMobile | boolean | – |
setOpenMobile | (open: boolean) => void | – |
isMobile | boolean | – |
toggleSidebarBascule la sheet sur téléphone, sinon la sidebar. | () => void | – |
- ButtonDes boutons dans toutes les variantes et tailles, avec un flux de chargement, de succès et d’erreur intégré qui évite le spinner pour les requêtes rapides.
- HotkeyAnalysez, libellez, annoncez et faites correspondre des raccourcis clavier, avec ⌘ sur les plateformes Apple et Ctrl partout ailleurs.
- InputUn champ de saisie texte en trois tailles, avec états invalide et lecture seule, style de validation natif et police tactile de 16px pour que les téléphones ne zooment jamais.
- SheetUn panneau qui glisse depuis n’importe quel bord, avec fermeture par balayage, verrouillage du défilement et imbrication empilée.
- SkeletonDes placeholders qui attendent 150ms avant de s’afficher, prennent la taille exacte du contenu qu’ils enveloppent et l’affichent en fondu sans rien déplacer.
- TooltipUne courte infobulle au survol ou au focus clavier, qui s’ouvre après un bref arrêt, passe instantanément d’un voisin à l’autre et affiche les raccourcis.
Utilisé dans les blocks
Des blocks qui s’appuient sur Sidebar.
- Chat SidebarLa barre latérale d’une application de chat. Logo, recherche et Nouveau chat en haut, vos propres liens en dessous, chats épinglés, projets qui se déploient pour montrer leurs chats, récents regroupés par jour, et des lignes avec menus au survol et au clic droit, renommage en ligne, suppression avec annulation et états de réponse en direct.
- HextaAIUne application de chat IA complète construite avec tous les blocks IA de HextaUI. Des conversations dans une barre latérale, la réflexion avec sources, des appels d’outils avec diffs et approbations, un plan à relire avant l’exécution de l’agent, du Markdown et du code en streaming, et un mode vocal silencieux, le tout piloté par les message parts d’AI SDK.