Sidebar
ShellNavegación lateral colapsable, con drawer automático en móvil y persistencia en cookie. El estado lo resuelve el servidor, así que no hay parpadeo al cargar.
No se renderiza en vivo en esta página. El sidebar es parte del layout de página completa (posicionamiento fijo, drawer móvil, cookie de estado) y montarlo suelto dentro de un preview de docs rompería el layout. Pruébalo en la demo en vivo.
Uso básico
Normalmente va dentro de un shell, que es quien reserva el espacio del contenido.
<x-kore::sidebar>
<x-slot:header>
<span class="kore-sidebar-label font-bold">Mi App</span>
<span class="kore-sidebar-mini font-bold">M</span>
</x-slot:header>
<x-kore::sidebar.item label="Panel" icon="layout-dashboard" route="dashboard" />
<x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" />
</x-kore::sidebar><x-kore::sidebar>
<x-slot:header>
<span class="kore-sidebar-label font-bold">Mi App</span>
<span class="kore-sidebar-mini font-bold">M</span>
</x-slot:header>
<x-kore::sidebar.item label="Panel" icon="layout-dashboard" route="dashboard" />
<x-kore::sidebar.item label="Usuarios" icon="users" route="users.index" />
</x-kore::sidebar>La marca al colapsar
Cuando el sidebar se reduce a iconos, el nombre completo ya no cabe.
Dos clases resuelven qué se ve en cada estado:
kore-sidebar-label— se ve con el sidebar ancho, y se desvanece al colapsar.kore-sidebar-mini— justo al revés: solo aparece en modo iconos.
<x-slot:header>
<img src="/logo.svg" class="kore-sidebar-label h-8" />
<img src="/isotipo.svg" class="kore-sidebar-mini h-8" />
</x-slot:header><x-slot:header>
<img src="/logo.svg" class="kore-sidebar-label h-8" />
<img src="/isotipo.svg" class="kore-sidebar-mini h-8" />
</x-slot:header>Sin la versión mini, el hueco de la cabecera se queda vacío al colapsar.
Persistencia: la cookie kore_sidebar
El estado se guarda en cookie, no en localStorage, por un motivo concreto: la cookie viaja al servidor.
Laravel la lee al renderizar y emite el HTML con el ancho correcto ya puesto — ver el detalle completo en Primeros pasos. Es el mismo patrón que usan shadcn/ui y Nuxt UI, y en Laravel sale gratis.
Modo rail
El sidebar se muestra como iconos y se expande por encima del contenido al pasar el ratón, sin desplazarlo.
<x-kore::sidebar :rail="true">
<x-kore::sidebar.item label="Panel" icon="layout-dashboard" route="dashboard" />
</x-kore::sidebar><x-kore::sidebar :rail="true">
<x-kore::sidebar.item label="Panel" icon="layout-dashboard" route="dashboard" />
</x-kore::sidebar>Ideal cuando el espacio horizontal es oro. También se expande al llegar con el teclado (Tab), no solo con el ratón: sin eso, quien navega tabulando no vería dónde está.
expandOnHover hace lo mismo pero partiendo de expandido en vez de colapsado.
Móvil: drawer con CSS puro
Por debajo del breakpoint, el sidebar se convierte en un drawer que entra desde el lado.
Con fondo oscuro y su botón de cierre. Se cierra al pulsar el fondo, con Escape, o al navegar con wire:navigate. Mientras está abierto, la página de detrás no hace scroll.
Todo esto es CSS, no JavaScript: es correcto en el primer paint y en cada cambio de tamaño de ventana. El drawer siempre se abre a ancho completo y con las etiquetas visibles, aunque en escritorio estuviera colapsado.
Varios sidebars a la vez
Con un id distinto y placement=right puedes tener un panel de herramientas a la derecha.
<x-kore::shell>
<x-slot:sidebar>
<x-kore::sidebar id="main">…</x-kore::sidebar>
</x-slot:sidebar>
<x-slot:aside>
<x-kore::sidebar id="tools" placement="right" width="20rem">…</x-kore::sidebar>
</x-slot:aside>
{{ $slot }}
</x-kore::shell><x-kore::shell>
<x-slot:sidebar>
<x-kore::sidebar id="main">…</x-kore::sidebar>
</x-slot:sidebar>
<x-slot:aside>
<x-kore::sidebar id="tools" placement="right" width="20rem">…</x-kore::sidebar>
</x-slot:aside>
{{ $slot }}
</x-kore::shell>Cada uno recuerda su estado por separado, y el shell reserva el espacio de ambos.
Flyouts en modo iconos
Con el sidebar colapsado, los sub-items no caben en línea.
Salen en un panel flotante al pasar el ratón. Los menús anidados se abren uno al lado del otro: entrar en un sub-menú de segundo nivel no cierra el panel del primero. Los items sin hijos muestran su nombre en un tooltip — hay uno solo por sidebar, reposicionado según el item activo, en vez de veinte tooltips montados a la vez.
Ver el detalle completo de sub-menús en sidebar.item.
Badges al colapsar
Un badge numérico se muda a la esquina del icono al colapsar el sidebar — es el único hueco que queda.
Por encima de badge_max (configurable, default 99), el contador se muestra como "99+". Un texto corto (!, new) pasa tal cual; cualquier otra cosa se degrada a un punto, para al menos dejar constancia de que ese item tiene algo.
El valor que se acorta es solo el visual. Un lector de pantalla sigue anunciando el número real, no 99+. Ver la API completa (badge, badgeVariant, badgeColor, badgeMax) en sidebar.item.
Store de Alpine
El estado vive en $store.koreSidebar, así que se puede leer y cambiar desde cualquier parte de la página, sin estar anidado dentro del sidebar.
| Método | Descripción |
|---|---|
toggle(id) | Colapsar / expandir (escritorio) |
setCollapsed(id, bool) | Fijar el estado |
isCollapsed(id) | ¿Está en modo iconos? |
openMobile(id) / closeMobile(id) | Abrir / cerrar el drawer |
isOpen(id) | ¿El drawer está abierto? |
isMobile(id) | ¿El viewport está por debajo del breakpoint? |
handleToggle(id) | Colapsa en escritorio y abre el drawer en móvil. Es lo que quiere un botón de menú |
<button x-data x-on:click="$store.koreSidebar.toggle('main')">
Colapsar
</button>
<div x-data x-show="$store.koreSidebar.isOpen('main')">
El drawer está abierto
</div><button x-data x-on:click="$store.koreSidebar.toggle('main')">
Colapsar
</button>
<div x-data x-show="$store.koreSidebar.isOpen('main')">
El drawer está abierto
</div>Eventos
Se despachan sobre window.
| Evento | Payload |
|---|---|
kore:sidebar-toggle | { id, collapsed } |
kore:sidebar-mobile-open | { id } |
kore:sidebar-mobile-close | { id } |
Props
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
id
|
string | main | Identifica el sidebar en la cookie y en el store. Necesario si hay más de uno |
collapsible
|
bool | config(true) | Permite colapsarlo a modo iconos |
collapsed
|
bool | config(false) | Estado en la primera visita, antes de que exista cookie |
placement
|
left|right | config(left) | Lado de la pantalla |
width
|
string | config(16rem) | Ancho expandido. Longitud CSS, no clase de Tailwind |
collapsedWidth
|
string | config(4rem) | Ancho en modo iconos |
breakpoint
|
sm|md|lg|xl | config(lg) | Por debajo de esto, el sidebar es un drawer |
persist
|
bool | config(true) | Recordar el estado entre visitas (cookie kore_sidebar) |
smart
|
bool|null | config(true) | Detectar sola la ruta activa. Lo hereda cada item: manda el item, luego el sidebar, luego la configuración |
navigate
|
bool|null | config(false) | Añadir wire:navigate a los enlaces. Misma herencia que smart |
overlay
|
bool | config(true) | Fondo oscuro tras el drawer móvil |
rail
|
bool | config(false) | Modo rail: solo iconos, se expande sobre el contenido al pasar el ratón |
expandOnHover
|
bool | config(false) | Como rail, pero partiendo de expandido |
ariaLabel
|
string | Sidebar | Nombre de la región de navegación |
Configuración
Personaliza los defaults en config/kore-ui.php, sección shell.sidebar.
'shell' => [
'sidebar' => [
'collapsible' => true,
'collapsed' => false, // estado en la primera visita, antes de que haya cookie
'placement' => 'left', // 'left' | 'right'
'width' => '16rem',
'collapsed_width' => '4rem',
'breakpoint' => 'lg', // por debajo de esto, el sidebar es un drawer
'persist' => true,
'smart' => true, // detectar sola la ruta activa (llega a los items desde la 2.2)
'navigate' => false, // añadir wire:navigate a los enlaces
'overlay' => true, // fondo oscuro tras el drawer móvil
'rail' => false,
'expand_on_hover' => false,
'duration' => 200, // ms de la animación
'badge_max' => 99, // por encima, el contador muestra "99+"
],
],'shell' => [
'sidebar' => [
'collapsible' => true,
'collapsed' => false, // estado en la primera visita, antes de que haya cookie
'placement' => 'left', // 'left' | 'right'
'width' => '16rem',
'collapsed_width' => '4rem',
'breakpoint' => 'lg', // por debajo de esto, el sidebar es un drawer
'persist' => true,
'smart' => true, // detectar sola la ruta activa (llega a los items desde la 2.2)
'navigate' => false, // añadir wire:navigate a los enlaces
'overlay' => true, // fondo oscuro tras el drawer móvil
'rail' => false,
'expand_on_hover' => false,
'duration' => 200, // ms de la animación
'badge_max' => 99, // por encima, el contador muestra "99+"
],
],Los anchos son longitudes CSS, no clases de Tailwind: alimentan las custom properties que gobiernan el layout.