Sidebar
Una barra lateral de la app que se contrae a iconos o fuera del lienzo, se mantiene fija bajo tu encabezado y se convierte en una hoja deslizable en móviles.
pnpm dlx shadcn@latest add https://hextaui.com/r/sidebar.jsonAñade el componente, los tokens del tema de HextaUI y los componentes de HextaUI de los que depende.
Añade los tokens del tema a tu CSS global, si aún no lo has hecho.
Instala las dependencias.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopia y pega el siguiente código en tu proyecto.
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 Actualiza las rutas de importación para que coincidan con la configuración de tu proyecto.
Envuelve tu diseño en <SidebarProvider /> y pon la página en <SidebarInset />, después del sidebar.
SidebarProvidercontiene el estado de apertura, el atajo de teclado y los anchos.Sidebares una columna que permanece fija mientras la página se desplaza. Por debajo de 768px se convierte en un sheet.SidebarHeaderySidebarFooterpermanecen en su sitio.SidebarContentse desplaza entre ellos.SidebarGroupes una sección con una etiqueta y una acción opcionales.SidebarMenucontiene los enlaces.SidebarInsetes la página junto al sidebar.
Variantes
variant define el aspecto: un sidebar de altura completa, un panel floating, o inset, donde la página se convierte en una tarjeta sobre el color del sidebar. Alterna entre ellos para ver cómo se anima el diseño.
Collapsible
offcanvas desliza el sidebar fuera de la vista, icon lo reduce a sus iconos y muestra cada etiqueta en un tooltip, y none lo mantiene abierto. Pulsa ⌘B o Ctrl+B para alternar el sidebar en el que estás trabajando.
Grupos colapsables y submenús
Envuelve un SidebarGroup o un SidebarMenuItem en un Collapsible, renderiza la etiqueta o el botón como trigger y anida un SidebarMenuSub.
Lado derecho
Define side="right" y coloca el sidebar después de SidebarInset. El rail y el sheet móvil siguen el lado.
Bajo un encabezado
El sidebar es sticky, así que empieza debajo de lo que haya encima. Con un encabezado sticky, define --sidebar-top con su altura y el sidebar se fija debajo y se ajusta al resto de la pantalla.
Controlado
Pasa open y onOpenChange. El trigger, el rail y el atajo pasan todos por onOpenChange.
Cargando
SidebarMenuSkeleton rellena un menú mientras carga. Los anchos varían por fila y coinciden entre servidor y cliente.
De derecha a izquierda
Define dir="rtl" y side="right". El espaciado, las líneas de submenú, los tooltips y el icono del trigger se reflejan.
El sidebar mide 16rem de ancho, 18rem en teléfonos y 3rem cuando está colapsado a iconos. Sobrescribe --sidebar-width, --sidebar-width-mobile y --sidebar-width-icon en el provider.
El sidebar no tiene efectos secundarios. Para recordar si estaba abierto, guárdalo en onOpenChange y vuelve a leerlo en defaultOpen al renderizar en el servidor.
| Key | Acción |
|---|---|
| ⌘ + BCtrl + B | Alterna el sidebar. Con varios sidebars en una página, responde el que tiene el foco y, si no, el primero. Se ignora mientras se escribe en un editor de texto enriquecido. |
| TabShift + Tab | Recorre los enlaces. Se omite un sidebar colapsado fuera del lienzo. |
| EnterSpace | Activa el enlace, botón o trigger enfocado. |
| Esc | Cierra el sidebar en teléfonos. |
SidebarTriggertiene la etiqueta “Toggle Sidebar” y exponearia-expandedyaria-controls.isActivedefinearia-current="page".- Colapsar fuera del lienzo oculta el contenido al teclado y a los lectores de pantalla. Si el foco estaba dentro, pasa al trigger.
- Colapsado a iconos, las etiquetas permanecen en el nombre accesible de cada enlace y se muestran como tooltip al pasar el cursor y al enfocar con teclado. Las etiquetas de grupo, las acciones y los badges se ocultan.
- En teléfonos el sidebar es un sheet modal: el foco queda atrapado, deslizar o Esc lo cierra, y seguir un enlace lo cierra. Los enlaces que abren una pestaña nueva, las descargas y los clics con modificadores lo mantienen abierto.
- Con movimiento reducido activado, el colapso es instantáneo.
| Prop | Tipo | Predeterminado |
|---|---|---|
defaultOpen | boolean | true |
open | boolean | – |
onOpenChange | (open: boolean) => void | – |
keyboardShortcutmod es ⌘ en plataformas Apple y Ctrl en el resto. null lo desactiva. | string | null | "mod+b" |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-wrapper" | El contenedor del diseño. |
--sidebar-width | Ancho expandido. Por defecto es 16rem. |
--sidebar-width-mobile | Ancho del sheet del teléfono. Por defecto es 18rem. |
--sidebar-width-icon | Ancho cuando está colapsado a iconos. Por defecto es 3rem. |
--sidebar-top | Dónde se fija el sidebar al hacer scroll, por ejemplo debajo de un encabezado sticky. Por defecto es 0px. |
className y las demás props van al contenedor del sidebar.
| Prop | Tipo | Predeterminado |
|---|---|---|
side | "left" | "right" | "left" |
variantplain elimina la superficie, la línea del borde, la barra de scroll y el espacio entre elementos del menú, para un sidebar que se asienta sobre la página. | "sidebar" | "floating" | "inset" | "plain" | "sidebar" |
collapsible | "offcanvas" | "icon" | "none" | "offcanvas" |
mobileCómo se abre en teléfonos: un sheet desde el lateral, o deslizándose para llenar la pantalla, como en las apps de chat. | "sheet" | "fullscreen" | "sheet" |
dirTambién define la dirección del sheet en teléfonos. | "ltr" | "rtl" | – |
| Atributo | Descripción |
|---|---|
data-slot="sidebar" | El sidebar, o el sheet en teléfonos. |
data-state | "expanded" o "collapsed". |
data-collapsible | El modo colapsable mientras está colapsado, y "" en caso contrario. Da estilo a los hijos con group-data-[collapsible=icon]:. |
data-variant | La variante. |
data-side | El lado. |
data-mobile | Presente en el sheet del teléfono. |
data-slot="sidebar-container" | El panel que se desliza y cambia de tamaño. |
data-slot="sidebar-inner" | La superficie que contiene el contenido. |
Un Button ghost con icono que llama a toggleSidebar. Llama a event.preventDefault() en onClick para detenerlo. Pasa hijos para sustituir el icono.
| Atributo | Descripción |
|---|---|
data-slot="sidebar-trigger" | El trigger. |
aria-expanded | Si el sidebar está abierto. |
Un área de toque delgada en el borde del sidebar que lo alterna con un clic. Queda fuera del orden de tabulación porque el trigger y el atajo cubren a los usuarios de teclado. Cuando el sidebar está fuera del lienzo, el rail permanece en el borde de la pantalla.
| Atributo | Descripción |
|---|---|
data-slot="sidebar-rail" | El rail. |
Un <main> que ocupa el resto del ancho. Junto a un sidebar inset se convierte en una tarjeta redondeada.
| Prop | Tipo | Predeterminado |
|---|---|---|
renderRenderiza un elemento distinto. Pasa un <div /> cuando la página ya tiene un landmark <main>. | React.ReactElement | (props) => React.ReactElement | – |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-inset" | El área de la página. |
Elementos <div> simples. El contenido se desplaza con una barra de scroll fina y degradados suaves en los bordes.
| Atributo | Descripción |
|---|---|
data-slot="sidebar-header" | Sección superior. |
data-slot="sidebar-content" | Zona central con scroll. |
data-slot="sidebar-footer" | Sección inferior. |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-group" | Una sección del sidebar. |
data-slot="sidebar-group-content" | El contenido del grupo. |
| Prop | Tipo | Predeterminado |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <div> |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-group-label" | Se desliza hacia arriba y se desvanece cuando está colapsado a iconos. |
| Prop | Tipo | Predeterminado |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-group-action" | Necesita una etiqueta accesible. |
Un <ul> y sus elementos <li>. Define gap="none" para listas densas como el historial de chat, donde las filas van pegadas.
| Prop | Tipo | Predeterminado |
|---|---|---|
gapEspacio entre elementos. También en SidebarMenuSub. | "default" | "none" | "default" |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-menu" | La lista. |
data-slot="sidebar-menu-item" | Un elemento. group/menu-item para estilos de hover. |
| Prop | Tipo | Predeterminado |
|---|---|---|
isActiveLo resalta y define aria-current="page". | boolean | false |
variant | "default" | "outline" | "default" |
size | "default" | "sm" | "lg" | "default" |
tooltipSe muestra cuando está colapsado a iconos. El contenido se desliza entre elementos a medida que te mueves por el menú. | ReactNode | TooltipContentProps | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-menu-button" | peer/menu-button para estilos de elementos hermanos. |
data-active | Presente mientras isActive. |
data-size | El tamaño. |
| Prop | Tipo | Predeterminado |
|---|---|---|
showOnHoverLo muestra solo mientras el elemento tiene hover o foco, o su menú está abierto. Siempre visible en pantallas táctiles. | boolean | false |
render | ReactElement | (props, state) => ReactElement | <button> |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-menu-action" | Necesita una etiqueta accesible. |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-menu-badge" | Un contador al final del elemento. |
| Prop | Tipo | Predeterminado |
|---|---|---|
showIcon | boolean | false |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-menu-skeleton" | Oculto a las tecnologías de asistencia. |
Una lista anidada con una línea en su borde de inicio. Se pliega cuando el sidebar se colapsa a iconos.
| Prop | Tipo | Predeterminado |
|---|---|---|
isActiveEn SidebarMenuSubButton. | boolean | false |
sizeEn SidebarMenuSubButton. | "sm" | "md" | "md" |
renderEn SidebarMenuSubButton. | ReactElement | (props, state) => ReactElement | <a> |
| Atributo | Descripción |
|---|---|
data-slot="sidebar-menu-sub" | La lista anidada. |
data-slot="sidebar-menu-sub-button" | Un enlace anidado. |
data-active | Presente mientras isActive. |
Un Input pequeño sobre el fondo de la página, y un separador de línea fina.
| Atributo | Descripción |
|---|---|
data-slot="sidebar-input" | El input. |
data-slot="sidebar-separator" | El separador. |
Lee y controla el sidebar más cercano. Lanza un error fuera de SidebarProvider.
| Prop | Tipo | Predeterminado |
|---|---|---|
state | "expanded" | "collapsed" | – |
open | boolean | – |
setOpen | (open: boolean) => void | – |
openMobile | boolean | – |
setOpenMobile | (open: boolean) => void | – |
isMobile | boolean | – |
toggleSidebarAlterna el sheet en teléfonos y, en los demás casos, el sidebar. | () => void | – |
- ButtonBotones en todas las variantes y tamaños, con un flujo integrado de carga, éxito y error que omite el spinner en las peticiones rápidas.
- HotkeyAnaliza, etiqueta, anuncia y reconoce atajos de teclado, con ⌘ en plataformas Apple y Ctrl en el resto.
- InputUn campo de texto con tres tamaños, estados inválido y de solo lectura, estilos de validación nativos y una fuente táctil de 16px para que los móviles nunca hagan zoom.
- SheetUn panel que se desliza desde cualquier borde, con deslizamiento para descartar, bloqueo de scroll y anidamiento apilado.
- SkeletonMarcadores de posición que esperan 150ms antes de mostrarse, toman el tamaño exacto del contenido que envuelven y lo hacen aparecer con un fundido sin mover nada.
- TooltipUna pista breve al pasar el cursor o enfocar con el teclado que se abre tras un breve reposo, cambia al instante entre vecinos y muestra atajos.
Usado en bloques
Bloques que se construyen sobre Sidebar.
- Chat SidebarLa barra lateral de una app de chat. Logo, búsqueda y Nuevo chat arriba, tus propios enlaces debajo, chats fijados, proyectos que se expanden para mostrar sus chats, recientes agrupados por día y filas con menús al pasar el cursor y con clic derecho, renombrado en línea, eliminación con deshacer y estados de respuesta en vivo.
- HextaAIUna app de chat de IA completa construida con todos los bloques de IA de HextaUI. Chats en una barra lateral, razonamiento con fuentes, llamadas a herramientas con diffs y aprobaciones, un plan que revisas antes de que el agente se ejecute, Markdown y código en streaming y modo de voz silencioso, todo impulsado por las message parts de AI SDK.