Skip to Content

Sidebar

A collapsible sidebar layout commonly used for application navigation. Built using Box, Column, Row, Icon, Text, and Button components.

Acme Inc
Dashboard
Users
Documents
Settings
John DoeAdmin
Main content area

Source

'use client'; import { createContext, useContext, useState } from 'react'; import { Box, Column, Row, Icon, Text, Button } from '@umami/react-zen'; import { PanelLeft } from 'lucide-react'; const SidebarContext = createContext({ isCollapsed: false, setCollapsed: () => {}, }); export function useSidebar() { return useContext(SidebarContext); } export function Sidebar({ defaultCollapsed = false, collapsedWidth = '4rem', expandedWidth = '14rem', children, ...props }) { const [isCollapsed, setCollapsed] = useState(defaultCollapsed); return ( <SidebarContext.Provider value={{ isCollapsed, setCollapsed }}> <Box height="100%" backgroundColor="surface-raised" borderColor="muted" border="right" style={{ width: isCollapsed ? collapsedWidth : expandedWidth, transition: 'width 0.2s ease-in-out', }} {...props} > <Column height="100%" justifyContent="space-between"> {children} </Column> </Box> </SidebarContext.Provider> ); } export function SidebarHeader({ icon, title }) { const { isCollapsed } = useSidebar(); return ( <Row padding="4" gap="3" alignItems="center" borderColor="muted" border="bottom" justifyContent={isCollapsed ? 'center' : 'flex-start'} > {icon && ( <Box width="8" height="8" borderRadius="md" backgroundColor="primary" flexShrink="0" > {icon} </Box> )} {!isCollapsed && title && <Text weight="semibold">{title}</Text>} </Row> ); } export function SidebarNav({ children }) { return ( <Column padding="2" gap="1" flexGrow="1"> {children} </Column> ); } export function SidebarItem({ icon, label, isSelected, onClick }) { const { isCollapsed } = useSidebar(); return ( <Row paddingY="2" paddingX="3" borderRadius="md" gap="3" alignItems="center" justifyContent={isCollapsed ? 'center' : 'flex-start'} backgroundColor={isSelected ? 'interactive' : undefined} className={`cursor-pointer ${!isSelected ? 'hover:bg-interactive' : ''}`} onClick={onClick} > <Icon size="sm" color={isSelected ? undefined : 'muted'}> {icon} </Icon> {!isCollapsed && ( <Text color={isSelected ? undefined : 'muted'}> {label} </Text> )} </Row> ); } export function SidebarFooter({ children }) { return ( <Box padding="4" borderColor="muted" border="top"> {children} </Box> ); } export function SidebarToggle() { const { isCollapsed, setCollapsed } = useSidebar(); return ( <Button variant="quiet" onPress={() => setCollapsed(!isCollapsed)}> <Icon style={{ transform: isCollapsed ? 'scaleX(-1)' : undefined }}> <PanelLeft /> </Icon> </Button> ); }

Variations

With section dividers

Acme Inc
MAIN
Dashboard
Users
SETTINGS
General
Security
Main content area

Always collapsed (icon-only)

Main content area

Minimal with toggle only

Home
Search
Notifications
Main content area