Sidebar
A composable, themeable and customizable sidebar component.
A sidebar that collapses to icons.
Sidebars are one of the most complex components to build. They are central to any application and often contain a lot of moving parts.
We now have a solid foundation to build on top of. Composable. Themeable. Customizable.
Installation
Structure
A Sidebar component is composed of the following parts:
Sidebar.Provider- Handles collapsible state.Sidebar- The sidebar container.Sidebar.HeaderandSidebar.Footer- Sticky at the top and bottom of the sidebar.Sidebar.Content- Scrollable content.Sidebar.Group- Section within theSidebar.Content.Sidebar.Trigger- Trigger for theSidebar.
Usage
module Layout = {
@react.component
let make = (~children) =>
<Sidebar.Provider>
<AppSidebar />
<main>
<Sidebar.Trigger />
{children}
</main>
</Sidebar.Provider>
}@react.component
let make = () =>
<Sidebar>
<Sidebar.Header />
<Sidebar.Content>
<Sidebar.Group />
<Sidebar.Group />
</Sidebar.Content>
<Sidebar.Footer />
</Sidebar>Sidebar.Provider
The Sidebar.Provider component is used to provide the sidebar context to the Sidebar component. You should always wrap your application in a Sidebar.Provider component.
Props
Width
If you have a single sidebar in your application, you can use the sidebarWidth and sidebarWidthMobile variables in Sidebar.res to set the width of the sidebar.
let sidebarWidth = "16rem"
let sidebarWidthMobile = "18rem"For multiple sidebars in your application, you can use the --sidebar-width and --sidebar-width-mobile CSS variables in the style prop.
<Sidebar.Provider
style={{
"--sidebar-width": "20rem",
"--sidebar-width-mobile": "20rem",
}}
>
<Sidebar />
</Sidebar.Provider>Keyboard Shortcut
To trigger the sidebar, you use the cmd+b keyboard shortcut on Mac and ctrl+b on Windows.
let sidebarKeyboardShortcut = "b"Sidebar
The main Sidebar component used to render a collapsible sidebar.
Props
Note: If you use the inset variant, remember to wrap your main content
in a Sidebar.Inset component.
<Sidebar.Provider>
<Sidebar variant=Inset />
<Sidebar.Inset>
<main>{children}</main>
</Sidebar.Inset>
</Sidebar.Provider>Sidebar.use
The Sidebar.use hook is used to control the sidebar.
module AppSidebar = {
@react.component
let make = () => {
let {
state,
open,
setOpen,
openMobile,
setOpenMobile,
isMobile,
toggleSidebar,
} = Sidebar.use()
}
}Sidebar.Header
Use the Sidebar.Header component to add a sticky header to the sidebar.
<Sidebar>
<Sidebar.Header>
<Sidebar.Menu>
<Sidebar.MenuItem>
<DropdownMenu>
<DropdownMenu.Trigger render={<Sidebar.MenuButton/>}>
{"Select Workspace"->React.string}
<Icons.ChevronDown className="ml-auto" />
</DropdownMenu.Trigger>
<DropdownMenu.Content className="w-[--radix-popper-anchor-width]">
<DropdownMenu.Item>
<span> {"Acme Inc"->React.string} </span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Sidebar.MenuItem>
</Sidebar.Menu>
</Sidebar.Header>
</Sidebar>Sidebar.Footer
Use the Sidebar.Footer component to add a sticky footer to the sidebar.
<Sidebar>
<Sidebar.Footer>
<Sidebar.Menu>
<Sidebar.MenuItem>
<Sidebar.MenuButton>
<User2 /> Username
</Sidebar.MenuButton>
</Sidebar.MenuItem>
</Sidebar.Menu>
</Sidebar.Footer>
</Sidebar>Sidebar.Content
The Sidebar.Content component is used to wrap the content of the sidebar. This is where you add your Sidebar.Group components. It is scrollable.
<Sidebar>
<Sidebar.Content>
<Sidebar.Group />
<Sidebar.Group />
</Sidebar.Content>
</Sidebar>Sidebar.Group
Use the Sidebar.Group component to create a section within the sidebar.
A Sidebar.Group has a Sidebar.GroupLabel, a Sidebar.GroupContent and an optional Sidebar.GroupAction.
<Sidebar.Group>
<Sidebar.GroupLabel>Application</Sidebar.GroupLabel>
<Sidebar.GroupAction>
<Plus /> <span className="sr-only">Add Project</span>
</Sidebar.GroupAction>
<Sidebar.GroupContent></Sidebar.GroupContent>
</Sidebar.Group>To make a Sidebar.Group collapsible, wrap it in a Collapsible.
<Collapsible defaultOpen className="group/collapsible">
<Sidebar.Group>
<Sidebar.GroupLabel render={<Collapsible.Trigger/>}>
{"Help"->React.string}
<Icons.ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
</Sidebar.GroupLabel>
<Collapsible.Content>
<Sidebar.GroupContent />
</Collapsible.Content>
</Sidebar.Group>
</Collapsible>Sidebar.Menu
The Sidebar.Menu component is used for building a menu within a Sidebar.Group.
<Sidebar.Menu>
{projects.map((project) => (
<Sidebar.MenuItem key={project.name}>
<Sidebar.MenuButton render={<a href={project.url}/>}>
<Project.Icon />
<span>{project.name->React.string}</span>
</Sidebar.MenuButton>
</Sidebar.MenuItem>
))}
</Sidebar.Menu>Sidebar.MenuButton
The Sidebar.MenuButton component is used to render a menu button within a Sidebar.MenuItem.
By default, the Sidebar.MenuButton renders a button but you can use the render prop to render a different component such as a Link or an a tag.
Use the isActive prop to mark a menu item as active.
<Sidebar.MenuButton render={<a href="#"/>} isActive>
{"Home"->React.string}
</Sidebar.MenuButton>Sidebar.MenuAction
The Sidebar.MenuAction component is used to render a menu action within a Sidebar.MenuItem.
<Sidebar.MenuItem>
<Sidebar.MenuButton render={<a href="#"/>}>
<Home />
<span> {"Home"->React.string} </span>
</Sidebar.MenuButton>
<Sidebar.MenuAction>
<Icons.Plus /> <span className="sr-only">Add Project</span>
</Sidebar.MenuAction>
</Sidebar.MenuItem>Sidebar.MenuSub
The Sidebar.MenuSub component is used to render a submenu within a Sidebar.Menu.
<Sidebar.MenuItem>
<Sidebar.MenuButton />
<Sidebar.MenuSub>
<Sidebar.MenuSubItem>
<Sidebar.MenuSubButton />
</Sidebar.MenuSubItem>
</Sidebar.MenuSub>
</Sidebar.MenuItem>Sidebar.MenuBadge
The Sidebar.MenuBadge component is used to render a badge within a Sidebar.MenuItem.
<Sidebar.MenuItem>
<Sidebar.MenuButton />
<Sidebar.MenuBadge>24</Sidebar.MenuBadge>
</Sidebar.MenuItem>Sidebar.MenuSkeleton
The Sidebar.MenuSkeleton component is used to render a skeleton for a Sidebar.Menu.
<Sidebar.Menu>
{Array.fromInitializer(~length: 5 )->Array.mapWithIndex((_, index) => (
<Sidebar.MenuItem key={index->Int.toString}>
<Sidebar.MenuSkeleton />
</Sidebar.MenuItem>
))}
</Sidebar.Menu>Sidebar.Trigger
Use the Sidebar.Trigger component to render a button that toggles the sidebar.
module CustomTrigger = {
@react.component
let make = () => {
let { toggleSidebar } = Sidebar.use()
<button onClick={_ => toggleSidebar()}> {"Toggle Sidebar"->React.string} </button>
}
}Sidebar.Rail
The Sidebar.Rail component is used to render a rail within a Sidebar. This rail can be used to toggle the sidebar.
<Sidebar>
<Sidebar.Header />
<Sidebar.Content>
<Sidebar.Group />
</Sidebar.Content>
<Sidebar.Footer />
<Sidebar.Rail />
</Sidebar>Controlled Sidebar
Use the open_ and onOpenChange props to control the sidebar.
module AppSidebar = {
@react.component
let make = () => {
let (open_, setOpen) = React.useState(() => false)
<Sidebar.Provider open_ onOpenChange={o => setOpen(_ => o)}>
<Sidebar />
</Sidebar.Provider>
}
}Theming
We use the following CSS variables to theme the sidebar.
@layer base {
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}Styling
Here are some tips for styling the sidebar based on different states.
<Sidebar collapsible="icon">
<Sidebar.Content>
<Sidebar.Group className="group-data-[collapsible=icon]:hidden" />
</Sidebar.Content>
</Sidebar><Sidebar.MenuItem>
<Sidebar.MenuButton />
<Sidebar.MenuAction className="peer-data-[active=true]/menu-button:opacity-100" />
</Sidebar.MenuItem>