O componente Sidebar é uma sidebar fixa e independente que empurra o conteúdo da página. No desktop, ela é renderizada inline e pode ser recolhida; no mobile, ela abre um componente Modal, Slideover ou Drawer.
Use os slots header, default e footer para personalizar o conteúdo da sidebar. A diretiva v-model:open é sensível à viewport: no desktop ela controla o estado expandido/recolhido, no mobile ela controla o menu.
Use a prop variant para alterar o estilo visual da sidebar. O padrão é sidebar.
Use a prop collapsible para alterar o comportamento de recolhimento da sidebar. O padrão é offcanvas.
offcanvas: The sidebar slides out of view completely.icon: The sidebar shrinks to icon-only width.none: The sidebar is not collapsible.state nas props de slot para personalizar o conteúdo da sidebar quando ela está recolhida.Use a prop side para alterar o lado da sidebar. O padrão é left.
Use a prop title para definir o título do cabeçalho da sidebar.
<template>
<NSidebar title="Navigation">
<Placeholder class="h-full" />
</NSidebar>
</template>
Use a prop description para definir a descrição do cabeçalho da sidebar.
<template>
<NSidebar title="Navigation" description="Browse your workspace">
<Placeholder class="h-full" />
</NSidebar>
</template>
Use a prop rail para exibir uma borda interativa fina na sidebar que alterna o estado recolhido ao clicar. O trilho só é renderizado quando collapsible não é none.
<template>
<NSidebar rail collapsible="icon" title="Navigation">
<Placeholder class="h-full" />
</NSidebar>
</template>
Use a prop close para exibir um botão de fechar no cabeçalho da sidebar. O botão de fechar só é renderizado quando collapsible não é none.
Você pode passar qualquer propriedade do componente Button para personalizá-lo.
<template>
<NSidebar close rail collapsible="icon" title="Navigation">
<Placeholder class="h-full" />
</NSidebar>
</template>
Use a prop close-icon para personalizar o Icon do botão de fechar. O padrão é i-lucide-x.
<template>
<NSidebar
close
close-icon="i-lucide-panel-right-close"
rail
collapsible="icon"
side="right"
title="Navigation"
>
<Placeholder class="h-full" />
</NSidebar>
</template>
Use a prop mode para alterar o modo do menu da sidebar no mobile. O padrão é slideover.
menu para personalizar o menu da sidebar; ele se adapta dependendo do modo que você escolher.Você pode controlar o estado de abertura usando a prop open ou a diretiva v-model:open. No desktop ela controla o estado expandido/recolhido, no mobile ela abre/fecha o menu deslizante.
defineShortcuts, você pode alternar o estado de abertura da Sidebar pressionando O.Use useLocalStorage do VueUse ou useCookie em vez de ref para persistir o estado da sidebar entre recarregamentos de página.
ref(true) por useLocalStorage('sidebar-open', true).A largura da sidebar é controlada pela variável CSS --sidebar-width (o padrão é 16rem). A largura do ícone recolhido é controlada por --sidebar-width-icon (o padrão é 4rem).
Sobrescreva-as globalmente no seu CSS ou por instância com o atributo style.
Para posicionar a sidebar abaixo de um Header, personalize o gap e o container usando a prop ui.
--ui-header-height tem o padrão 4rem e é usada pelo Header. Ajuste-a se a sua navbar usar uma altura diferente.Use a sidebar no lado direito com ChatMessages e ChatPrompt para criar um painel de chat de IA.
| Prop | Default | Type |
|---|---|---|
as | 'aside' | anyThe element or component this component should render as. |
variant | 'sidebar' | "floating" | "sidebar" | "inset"The visual variant of the sidebar. |
collapsible | 'offcanvas' | "offcanvas" | "icon" | "none"The collapse behavior of the sidebar.
|
side | 'left' | "left" | "right"The side to render the sidebar on. |
title | stringThe title displayed in the sidebar header. | |
description | stringThe description displayed in the sidebar header. | |
close | false | boolean | Omit<ButtonProps, LinkPropsKeys> Display a close button to collapse the sidebar.
Only renders when |
closeIcon | appConfig.ui.icons.close | anyThe icon displayed in the close button. |
rail | false | boolean Display a rail on the sidebar edge to toggle collapse.
Only renders when |
transition | true | boolean Animate the sidebar when collapsing or expanding. |
mode | 'slideover' | TThe mode of the sidebar menu on mobile. |
menu | SidebarMenu<T>The props for the sidebar menu component on mobile. | |
open | true | boolean |
ui | { root?: SlotClass; gap?: SlotClass; container?: SlotClass; inner?: SlotClass; header?: SlotClass; wrapper?: SlotClass; title?: SlotClass; description?: SlotClass; actions?: SlotClass; close?: SlotClass; body?: SlotClass; footer?: SlotClass; rail?: SlotClass; } |
| Slot | Type |
|---|---|
header | { state: SidebarState; open: boolean; close: () => void; } |
title | { state: SidebarState; } |
description | { state: SidebarState; } |
actions | { state: SidebarState; } |
close | { ui: object; } |
default | { state: SidebarState; open: boolean; close: () => void; } |
footer | { state: SidebarState; open: boolean; close: () => void; } |
rail | { ui: object; } |
content | { close: () => void; } |
export default defineAppConfig({
ui: {
sidebar: {
slots: {
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem]',
gap: 'relative w-(--sidebar-width) bg-transparent',
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) lg:flex',
inner: 'flex size-full flex-col overflow-hidden divide-y divide-default',
header: 'flex items-center gap-1.5 overflow-hidden px-4 min-h-(--ui-header-height)',
wrapper: 'min-w-0 flex-1',
title: 'text-highlighted font-semibold truncate',
description: 'text-muted text-sm truncate',
actions: 'flex items-center gap-1.5 shrink-0',
close: '',
body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
footer: 'flex items-center gap-1.5 overflow-hidden p-4',
rail: [
'absolute inset-y-0 z-20 hidden w-4 after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-border-accented)',
'after:transition-colors'
]
},
variants: {
transition: {
true: {
gap: 'transition-[width] duration-200 ease-out',
container: 'transition-[left,right,width] duration-200 ease-out',
rail: 'transition-all ease-out'
}
},
side: {
left: {
container: 'left-0 border-e border-default',
rail: 'end-0 translate-x-1/2'
},
right: {
container: 'right-0 border-s border-default',
rail: '-start-px -translate-x-1/2'
}
},
collapsible: {
offcanvas: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-0'
},
icon: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
container: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
actions: 'group-data-[state=collapsed]/sidebar:hidden',
body: 'group-data-[state=collapsed]/sidebar:overflow-hidden'
},
none: {
root: 'h-full w-(--sidebar-width)'
}
},
variant: {
sidebar: {},
floating: {
container: 'p-4 border-transparent',
inner: 'rounded-lg ring ring-default shadow-lg',
rail: 'inset-y-4'
},
inset: {
container: 'py-4 border-transparent',
inner: 'divide-transparent',
rail: 'inset-y-4'
}
}
},
compoundVariants: [
{
side: 'left',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-w-resize data-[state=collapsed]:cursor-e-resize'
}
},
{
side: 'right',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-e-resize data-[state=collapsed]:cursor-w-resize'
}
},
{
side: 'left',
collapsible: 'none',
class: {
root: 'border-e border-default'
}
},
{
side: 'right',
collapsible: 'none',
class: {
root: 'border-s border-default'
}
},
{
side: 'left',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-left-(--sidebar-width)'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-right-(--sidebar-width)'
}
},
{
variant: 'floating',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8)+2px)]'
}
},
{
variant: 'floating',
collapsible: 'none',
class: {
root: 'p-4 border-0'
}
},
{
variant: 'inset',
collapsible: 'none',
class: {
root: 'py-4 border-0'
}
},
{
variant: 'floating',
side: 'left',
class: {
rail: 'end-4'
}
},
{
variant: 'floating',
side: 'right',
class: {
rail: 'start-[calc(--spacing(4)-1px)]'
}
}
]
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
sidebar: {
slots: {
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem]',
gap: 'relative w-(--sidebar-width) bg-transparent',
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) lg:flex',
inner: 'flex size-full flex-col overflow-hidden divide-y divide-default',
header: 'flex items-center gap-1.5 overflow-hidden px-4 min-h-(--ui-header-height)',
wrapper: 'min-w-0 flex-1',
title: 'text-highlighted font-semibold truncate',
description: 'text-muted text-sm truncate',
actions: 'flex items-center gap-1.5 shrink-0',
close: '',
body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
footer: 'flex items-center gap-1.5 overflow-hidden p-4',
rail: [
'absolute inset-y-0 z-20 hidden w-4 after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-border-accented)',
'after:transition-colors'
]
},
variants: {
transition: {
true: {
gap: 'transition-[width] duration-200 ease-out',
container: 'transition-[left,right,width] duration-200 ease-out',
rail: 'transition-all ease-out'
}
},
side: {
left: {
container: 'left-0 border-e border-default',
rail: 'end-0 translate-x-1/2'
},
right: {
container: 'right-0 border-s border-default',
rail: '-start-px -translate-x-1/2'
}
},
collapsible: {
offcanvas: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-0'
},
icon: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
container: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
actions: 'group-data-[state=collapsed]/sidebar:hidden',
body: 'group-data-[state=collapsed]/sidebar:overflow-hidden'
},
none: {
root: 'h-full w-(--sidebar-width)'
}
},
variant: {
sidebar: {},
floating: {
container: 'p-4 border-transparent',
inner: 'rounded-lg ring ring-default shadow-lg',
rail: 'inset-y-4'
},
inset: {
container: 'py-4 border-transparent',
inner: 'divide-transparent',
rail: 'inset-y-4'
}
}
},
compoundVariants: [
{
side: 'left',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-w-resize data-[state=collapsed]:cursor-e-resize'
}
},
{
side: 'right',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-e-resize data-[state=collapsed]:cursor-w-resize'
}
},
{
side: 'left',
collapsible: 'none',
class: {
root: 'border-e border-default'
}
},
{
side: 'right',
collapsible: 'none',
class: {
root: 'border-s border-default'
}
},
{
side: 'left',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-left-(--sidebar-width)'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-right-(--sidebar-width)'
}
},
{
variant: 'floating',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8)+2px)]'
}
},
{
variant: 'floating',
collapsible: 'none',
class: {
root: 'p-4 border-0'
}
},
{
variant: 'inset',
collapsible: 'none',
class: {
root: 'py-4 border-0'
}
},
{
variant: 'floating',
side: 'left',
class: {
rail: 'end-4'
}
},
{
variant: 'floating',
side: 'right',
class: {
rail: 'start-[calc(--spacing(4)-1px)]'
}
}
]
}
}
})
]
})