Components

Sidebar

Build a navigation drawer with rail (compact) and responsive mobile states.

UiSidebar is a navigation drawer composed from region sub-components. It tracks two independent states:

  • rail — compact, icons-only mode (a desktop concern). One-way; the parent controls it.
  • open — the responsive mobile slide-in. Supports v-model:open; on desktop the drawer is always visible.

Build a sidebar

<script setup lang="ts">
import { NuxtLink } from '#components'
import { ref } from 'vue'

const rail = ref(false) // compact (desktop)
const open = ref(false) // mobile drawer
</script>

<template>
  <UiSidebar bordered :rail="rail" v-model:open="open" aria-label="Main navigation">
    <template #header>
      <UiSidebarDropdown title="Acme" subtitle="Workspace" />
    </template>

    <template #body>
      <UiSidebarText text="Platform" />
      <UiSidebarLink :as="NuxtLink" to="/" text="Home" icon="&#xe88a;" tooltip-text="Home" />
      <UiSidebarLink :as="NuxtLink" to="/projects" text="Projects" icon="&#xe8ef;" />

      <UiSidebarGroup text="Account" collapsible :default-open="true" icon="&#xe853;">
        <UiSidebarLink :as="NuxtLink" to="/account" text="Profile" icon="&#xe853;" child />
        <UiSidebarLink :as="NuxtLink" to="/notifications" text="Notifications" icon="&#xe7f4;" child />
      </UiSidebarGroup>
    </template>

    <template #footer>
      <UiBadge text="v1.0.0" variant="outline" size="sm" />
    </template>
  </UiSidebar>
</template>

Toggle the mobile drawer

Wire the sidebar to a UiNavbarToggle in your navbar. The toggle is hidden below 1024px unless show-toggle-button is set. Its collapsed prop follows the rail, and rail only applies from 1024px while open only applies below it, so one boolean can drive both:

<template>
  <UiSidebar :rail="collapsed" v-model:open="collapsed">…</UiSidebar>
  <UiNavbarToggle show-toggle-button :collapsed="collapsed" @toggle="collapsed = !collapsed" />
</template>

UiSidebar props

PropTypeDefaultDescription
borderedbooleanfalseRight border instead of shadow.
railbooleanfalseCompact icons-only mode (one-way).
openbooleanfalseMobile open state (v-model:open).
widthstring | nullnullSets --cffy-sidebar-width.
ariaLabelstring'Main navigation'Landmark name.
customClassClassValue | null''Extra classes for the sidebar container.
headerClass / bodyClass / footerClassClassValue | nullnullExtra classes for the region wrappers.

Slots: header, body and footer, each rendered in its .drawer-header / .drawer-body / .drawer-footer region; a region without content is not rendered.

Emits: update:open.

Item components

ComponentKey props
UiSidebarTexttext
UiSidebarLinkas, to / href, text, icon, tooltipText, tooltipPlacement, child, active, disabled, ariaLabelledby; #badge slot after the text
UiSidebarGrouptext, icon, collapsible, defaultOpen
UiSidebarDropdowntitle, subtitle, interactive, placement

CSS variables

The drawer (.navigation-drawer) and its mobile overlay read these variables. Set them on :root or on a wrapper. bordered keeps its square corners and no shadow, and the rail keeps its larger brand image. Set the width with the width prop or --cffy-sidebar-width. Hover and pressed backgrounds come from the shared state layer.

:root {
  --cffy-sidebar-bg-color: var(--cffy-surface-container-low);
  --cffy-sidebar-item-active-bg-color: var(--cffy-primary-a10);
  --cffy-sidebar-item-active-color: var(--cffy-on-primary);
  --cffy-sidebar-item-active-icon-color: var(--cffy-on-primary);
}
VariableDefaultControls
--cffy-sidebar-width--cffy-container-2xs (18rem)Drawer width (the rail is half of it)
--cffy-sidebar-bg-color / --cffy-sidebar-color--cffy-background / --cffy-on-backgroundBackground and text
--cffy-sidebar-border-color--cffy-outline-surfaceEdge border of bordered, and the line beside child items
--cffy-sidebar-shadow--cffy-shadow-lgShadow
--cffy-sidebar-radius--cffy-radius-xlInner-edge corners (the screen edge stays square)
--cffy-sidebar-padding--cffy-space-16Drawer padding
--cffy-sidebar-spacing--cffy-space-8Padding and gaps of header, body, footer and items
--cffy-sidebar-brand-size2.5remBrand image and icon size
--cffy-sidebar-easing / --cffy-sidebar-duration--cffy-ease-emphasized / --cffy-duration-600Open, close and rail transitions
--cffy-sidebar-item-colormuted text toneItem and section-label text
--cffy-sidebar-item-font-size / --cffy-sidebar-item-font-weight--cffy-fs-3xs / --cffy-fw-500Item text
--cffy-sidebar-item-radius--cffy-shape-controlItem corners
--cffy-sidebar-item-active-color / --cffy-sidebar-item-active-bg-color--cffy-on-background / --cffy-surface-body-paneCurrent-route item
--cffy-sidebar-item-active-icon-color--cffy-primary-a10Leading icon of the current-route item
--cffy-sidebar-overlay-color / --cffy-sidebar-overlay-blurblack at 25% / 2pxOverlay behind the mobile drawer
Copyright © 2026