sidebar
A column beside the page that collapses to a strip or disappears, and slides in over the page where there is no room beside it.
<div className="flex">
<Sidebar title="Acme" description="Workspace" close rail footer={<Account />}>
<Nav />
</Sidebar>
<Main>{children}</Main>
</div>
<div class="flex">
<Sidebar title="Acme" description="Workspace" close rail>
<Nav />
<template #footer><Account /></template>
</Sidebar>
<Main><slot /></Main>
</div>
The default slot is the body, not a trigger. A Sidebar has no trigger — it is part of the page rather than something summoned onto it — so the default slot goes to the one region that is always there.
Two elements, and which is which
The panel people see is fixed, so a long page scrolls under a sidebar that stays put
and the sidebar’s own body scrolls independently. Beside it, an empty spacer in normal
flow is exactly as wide as the panel, and that is what keeps the page content from
starting underneath it. Collapsing animates both, and they have to agree.
There is one width, --sidebar-width, and one collapsed width, --sidebar-width-icon,
both declared on the root. A class can redefine either without touching the recipe.
<Sidebar className="[--sidebar-width:20rem]" />
<Sidebar class="[--sidebar-width:20rem]" />
Collapsing
collapsible decides what collapsing means. offcanvas takes the whole column away,
icon leaves a strip wide enough for the icons in the body, and none never collapses
and puts the sidebar back into normal flow.
variant decides how the surface sits against the page: sidebar is flush against it,
floating lifts a rounded card off it, and inset insets the surface and drops the
rules between its regions.
side docks it to the start or the end, mirroring under a right-to-left locale.
Two controls collapse it, and both are opt-in. rail draws a wide hit area with a
hairline in it along the outer edge, from lg up. close puts a button in the header.
One state, two viewports
Below lg the spacer is hidden and the panel is off-canvas unless it is open. That is
the whole of the narrow behaviour, and the state that drives it is the same one the wide
viewport uses: crossing into the narrow viewport remembers where the wide one was and
closes the panel, and crossing back puts the remembered value back. Without that, a
sidebar expanded by design would cover a phone’s content the moment the page loaded.
The state is controllable. React takes open with onOpenChange, or defaultOpen to
leave it alone; Vue takes v-model:open.
Nuxt UI reaches for a Slideover on a phone and renders the sidebar’s content a second time inside it. This one keeps a single tree and moves it, so the body’s scroll position and any open disclosure inside it survive a viewport change. The cost is that the narrow panel is not a focus trap, which is a trade a layout element can afford and a Dialog cannot.
Slots
The same word names the recipe slot, the data-slot attribute and the key in ui.
- base
- gap
- container
- inner
- header
- wrapper
- title
- description
- actions
- close
- body
- footer
- rail
- overlay
Variants
Read off the recipe, so these are the values that actually resolve. A Theme layer can change which one is the default; it cannot add a value.
- side
- startend
- variant
- sidebarfloatinginset
- collapsible
- offcanvasiconnone
- transition
- truefalse
Props
Taken by both adapters, read out of the component module. The same names and the same types work in React and Vue.
Vue only
Binds v-model:open.
- #default
- The scrolling middle of the column, usually a navigation.
- #footer
- A row along the bottom, usually an account or a theme switch.
- #actions
- Buttons in the header, beside the close button.