Glin UI
Primitive / Organismbeta

Sidebar

Collapsible app sidebar with icon rail, mobile sheet, Ctrl/Cmd+B shortcut, and persisted state.

Edit on GitHub

Installation

Install from
pnpm dlx @glinui/cli@latest add sidebar

Prefer the package?

Usage

The sidebar animates its width when it collapses. Width is a documented exception to the transform and opacity only motion rule, because the content column must reflow with it. The transition is disabled under prefers-reduced-motion.

Use containerClassName to control the docking element, for example static h-full when the sidebar lives inside a bounded frame instead of the viewport.

Variants

Every look of the shared vocabulary. glinr (the default) is a lifted rail with hairline separators and a raised pill for the active item; plain is flat and shadcn-like. Collapsed icon mode keeps the same pill as a circle.

import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from "@glinui/ui"
const variants = ["glinr", "plain", "solid", "soft", "outline", "ghost", "gradient", "glass"] as const
export function Demo() {
return variants.map((variant) => (
<SidebarProvider key={variant}>
<Sidebar variant={variant} collapsible="none">
<SidebarHeader>{variant}</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem><SidebarMenuButton isActive>Home</SidebarMenuButton></SidebarMenuItem>
<SidebarMenuItem><SidebarMenuButton>Inbox</SidebarMenuButton></SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</SidebarProvider>
))
}

Glass (opt-in)

Glass is opt-in and needs a colourful or photographic backdrop behind the rail. The frame supplies one here.

Press Ctrl/Cmd + B
Page content
import {
Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent,
SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton,
SidebarMenuItem, SidebarProvider, SidebarRail, SidebarTrigger
} from "@glinui/ui"
import { House, Gear } from "@phosphor-icons/react"
export function Layout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<Sidebar variant="glass" collapsible="icon">
<SidebarHeader>Glin Studio</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive tooltip="Home"><House /><span>Home</span></SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Settings"><Gear /><span>Settings</span></SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>Footer</SidebarFooter>
<SidebarRail />
</Sidebar>
<SidebarInset>
<SidebarTrigger />
{children}
</SidebarInset>
</SidebarProvider>
)
}

Off-canvas

Collapses fully; reopen with the trigger.

Press Ctrl/Cmd + B
Page content
import {
Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent,
SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton,
SidebarMenuItem, SidebarProvider, SidebarRail, SidebarTrigger
} from "@glinui/ui"
import { House, Gear } from "@phosphor-icons/react"
export function Layout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<Sidebar collapsible="offcanvas">
<SidebarHeader>Glin Studio</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive tooltip="Home"><House /><span>Home</span></SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Settings"><Gear /><span>Settings</span></SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>Footer</SidebarFooter>
<SidebarRail />
</Sidebar>
<SidebarInset>
<SidebarTrigger />
{children}
</SidebarInset>
</SidebarProvider>
)
}

Accessibility

  • The panel is a `nav` landmark with an accessible name.
  • The current page uses `aria-current="page"`; collapsible group labels expose `aria-expanded` and `aria-controls`.
  • On small screens the sidebar becomes a Sheet dialog with focus trap and Escape to close.
  • In icon mode, labels stay in the accessibility tree (visually hidden) and tooltips reveal them.

Keyboard Interactions

KeyAction
Ctrl/Cmd + BToggle the sidebar.
Tab / Shift + TabMove through menu buttons.
Enter / SpaceActivate a button or toggle a group.
EscapeClose the mobile sheet.

ARIA Attributes

  • `<nav aria-label>`
  • `aria-current="page"` on the active button
  • `aria-expanded` on trigger and collapsible group labels

Reduced Motion

Width and caret transitions are disabled; state changes apply instantly.

Affected properties

widthtransform (rotate)

API Reference

SidebarProvider

PropTypeRequiredDefaultDescription
defaultOpenbooleanNotrueInitial open state when uncontrolled.
open / onOpenChangeboolean / (open: boolean) => void--Controlled open state.
storageKeystring | null-"glin-sidebar-state"localStorage key for persisting desktop state. Null disables persistence.
collapsiblebooleanNofalseLet users collapse this group by activating its label.
PropTypeRequiredDefaultDescription
side"start" | "end"-startInline edge to dock to. Flips in RTL.
variant"glinr" | "plain" | "solid" | "soft" | "outline" | "ghost" | "gradient" | "glass"-ambient style (glinr)Surface look. Omit it for the ambient design style: a lifted rail with hairline separators and a raised pill for the active item. `plain` is flat, `glass` is opt-in and needs a backdrop. Legacy `default` follows the ambient style.
collapsiblebooleanNooffcanvasCollapse to nothing, to an icon rail, or never.
labelstring-SidebarAccessible name of the navigation landmark.
containerClassNamestring--Classes for the docking element, e.g. `static h-full` inside a bounded frame.
defaultOpenbooleanNotrueInitial open state for a collapsible group.

SidebarMenuButton

PropTypeRequiredDefaultDescription
isActiveboolean-falseMarks the current page and sets aria-current.
tooltipstring--Label shown on hover when collapsed to icons.
asChildboolean-falseRender a link element.
variant / size"default" | "outline" / "sm" | "md" | "lg"--Button style and height.
collapsiblebooleanNofalseLet users collapse this group by activating its label.
defaultOpenbooleanNotrueInitial open state for a collapsible group.

SidebarGroup

PropTypeRequiredDefaultDescription
collapsiblebooleanNofalseMakes the label a toggle with aria-expanded.
defaultOpenbooleanNotrueInitial open state of a collapsible group.

Other parts

PropTypeRequiredDefaultDescription
useSidebar(){ state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar }--Read and control the sidebar from any child.
collapsiblebooleanNofalseLet users collapse this group by activating its label.
defaultOpenbooleanNotrueInitial open state for a collapsible group.

Source

Import directly from the package or browse the source on GitHub. Click any file to view it.

Import

import { Sidebar } from "@glinui/ui"

Dependencies

@glinui/ui@phosphor-icons/react@radix-ui/react-slotclass-variance-authority

Generated API Snapshot

Beta

Auto-extracted from TypeScript source in packages/ui/src/components/sidebar.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.

Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata

Primary Props Type

SidebarProps

SidebarGroupProps

PropTypeRequiredDefaultDescription
collapsiblebooleanNofalseLet users collapse this group by activating its label.
defaultOpenbooleanNotrueInitial open state for a collapsible group.

SidebarMenuButtonProps

PropTypeRequiredDefaultDescription
asChildbooleanNofalse-
isActivebooleanNofalseMarks the current page. Sets aria-current="page".
size"sm" | "md" | "lg"No"md"Variant option from menuButtonVariants.
tooltipstringNo-Tooltip shown only while the sidebar is collapsed to icons.
variant"default" | "outline"No"default"Variant option from menuButtonVariants.

SidebarProps

PropTypeRequiredDefaultDescription
collapsible"offcanvas" | "icon" | "none"NofalseHow the sidebar collapses on desktop.
containerClassNamestringNo-Class names for the outer docking element (height, position). Defaults to sticky full viewport height.
labelstringNo"Sidebar"Accessible name for the navigation landmark.
side"start" | "end"No"start"Which inline edge the sidebar docks to.
variantPanelVariantPropNo-Surface look. Omit for the ambient design style (glinr by default): a lifted rail with hairline separators and a raised pill for the active item. `glass` is opt-in and needs a rich backdrop.