A sidebar is not a design problem, it is a state problem. The visible part, a column of icons and labels down the left edge, takes an afternoon. The rest of the week goes on what happens when the window narrows, when someone collapses it, when they reload the page, when they tab through it, and when a route two levels deep needs to light up its parent.
There are 34 public components on 21st with "sidebar" in the name, a short list next to the 476 with "hero" in it, and that ratio is the point: nobody ships a sidebar to impress you. It is app furniture. So the useful version of this article is the patterns that recur, the components the community actually saved, and then the states, which is where the time goes.
The rail that expands on hover
The most saved sidebar on 21st is Sidebar by Manu Arora at 2,635 bookmarks: an icon rail that expands on hover, responsive, dark mode included. Sidebar by Andrew Luo (548) is the same idea taken from Supabase's dashboard, and Sidebar Component by jshguo sits second on the whole list at 881.
The appeal is obvious: the content column stays wide, and the labels are one gesture away rather than one click away. Two things to keep in mind before you adopt it. Hover is not a gesture on a touchscreen and it is not a gesture on a keyboard, so the hover expansion has to be an accelerator on top of a real toggle, never the only way to read a label. And decide whether the expansion overlays the content or pushes it: pushing reflows the page on every mouse drift across the left edge, which is the sort of thing you stop noticing and your users do not.
The whole dashboard shell
Some of these are not a sidebar, they are the layout the sidebar lives in. Dashboard with Collapsible Sidebar by Sonu (836) ships the column plus stat cards, an activity feed and a theme toggle. Dashboard Sidebar by Arun Dass (654) is a dual-theme shell with multi-tier collapsible navigation and framed content areas.
That is the right thing to take for a prototype and the wrong thing once you have a design system, because you adopt spacing, radius and palette decisions along with the navigation. In the second case, lift the nav column and throw the cards away.
Groups, switchers and a resize handle
Past roughly seven destinations, grouping is the feature. SidebarShowcase by Ruixen UI (67) is the version with a team switcher pinned at the top and the links split into named sections, built on shadcn primitives. Animated Sidebar by Unlumen UI (30) adds the two details people ask for later: an animated indicator that follows the active item, and a drag handle to resize the column. If you ship the handle, persist the width next to the collapsed flag, otherwise it resets on every reload and reads as a bug.
What goes inside it
Worth separating from navigation entirely. Sidebar News by dubinc (549) is the block at the bottom of Dub's sidebar: stacked update cards with swipe to dismiss and a completion state. Whatsapp Sidebar by Manoj (73) is a different animal again, a list pane rather than a nav, which means search, scroll position to preserve and virtualisation once the list is long. Glassmorphism Sidebar by Dhileep Kumar GM (164) is a skin over the same skeleton, which is fine as long as you realise that is what you are buying.
The states a sidebar has to handle
This is the part that is not a component list, and it is why a sidebar takes longer than it looks.
Two widths, not one. Expanded and collapsed-to-icons are different layouts, not a CSS width transition. In icon mode the labels have to go somewhere: shadcn's primitive moves them into a tooltip that is shown only while the state is collapsed, and suppressed on mobile. There is also a third mode, offcanvas, where the column leaves the layout entirely rather than shrinking to a rail.
The mobile drawer is a dialog. Below the breakpoint the docked column becomes an overlay, and an overlay owes you focus trapped inside it, Escape to close, the page behind it inert and not scrolling, and focus returned to the trigger on close. Do not hand-roll that. shadcn swaps the sidebar for a Sheet (a Radix dialog) at the mobile breakpoint precisely so it inherits the behaviour. The desktop docked sidebar must not trap focus: it is part of the page, not a layer over it.
Persist the preference where the server can read it. If your app renders on a server, put the collapsed flag in a cookie. shadcn writes sidebar_state with a seven-day max age for exactly this reason: localStorage can only be read after hydration, so the first paint is the wrong width and the layout visibly jumps. A purely client-rendered app can use localStorage without the jump.
Active route highlighting is two jobs. The styling half is a data attribute (shadcn's isActive sets data-active). The accessibility half is aria-current="page", and the primitive does not set it for you, so a screen reader has no idea which item is current unless you add it. The matching half has its own traps: exact equality fails for nested routes, since /settings/billing should still light /settings, while a naive startsWith lights /settings for /settings-v2. Compare on segment boundaries.
Nested groups change shape when you collapse. An accordion inside the column works while it is expanded. In icon mode there is nothing to expand into, so a submenu has to become a flyout anchored to the icon, and the open or closed state of each group has to survive the collapse and the reload. Decide this before you add the second level rather than after.
Keyboard, focus and landmarks. A toggle shortcut (cmd or ctrl plus B in shadcn's default, one constant at the top of the file) has to not fire while someone is typing in an input. A sidebar collapsed offcanvas is still in the DOM, so its links stay in the tab order unless you hide them, and tabbing into an invisible sidebar is the most common bug in a hand-rolled one. The primitive gives you a main element for the content but renders the navigation as plain divs and lists, so the nav landmark, the skip link and aria-expanded on the trigger are yours to add.
Where else to look
The honest table, including where we are the wrong answer:
| Source | What it gives | Trade-off |
|---|---|---|
| shadcn/ui Sidebar | A genuine primitive: provider, cookie-persisted state, icon and offcanvas collapse, mobile Sheet, groups, submenus, badges, skeletons, keyboard toggle | It is unopinionated about routing and accessibility: active matching, aria-current, the nav landmark and the icon-mode flyout are still your code |
| Radix or Base UI | The dialog, tooltip and collapsible pieces a sidebar is assembled from, with the focus behaviour correct | No sidebar component at all, so the layout, the widths and the persistence are from scratch |
| MUI Drawer, Ant Design Layout.Sider | A batteries-included drawer with permanent, persistent and temporary variants, breakpoints handled | You take the whole design language and its bundle with it |
| 21st | Community sidebars, including hover-expand rails and complete dashboard shells you can preview live | Quality varies by author, and 34 components is a small pool next to a category like heroes |
The split that works in practice: shadcn for the mechanism, a community component for the composition and the look, your own code for the routing glue. Nobody sells you the routing glue.
Taking one
Every component page has a live preview and the source. Installing goes through the shadcn CLI against our registry:
The key comes from your 21st account and installs need a membership, so set API_KEY_21ST once in your shell and the same command works for anything in the catalogue. The 21st MCP does the same job from Claude, Cursor or Codex if you would rather not leave the editor.
Frequently asked
- What should a dashboard sidebar contain?
- The primary destinations, grouped into named sections once there are more than about seven of them, a workspace or team switcher at the top if the app has more than one context, and an account block pinned at the bottom. Anything that is not navigation, such as a changelog card, an upgrade prompt or a usage meter, belongs in a slot at the foot of the column rather than mixed into the link list. Keep the deepest useful level to two: a third level of nesting has nowhere to go once the sidebar collapses to icons.
- How do you build a collapsible sidebar in React?
- Treat collapsed as application state rather than a CSS class: hold it in a context provider so a trigger anywhere in the layout can toggle it, and give the collapsed layout its own rules, since labels have to become tooltips and submenus have to become flyouts anchored to the icon. shadcn/ui's Sidebar does this already, with collapsible="icon" for a narrow icon rail and collapsible="offcanvas" for removing the column entirely. Persist the preference in a cookie if the app renders on a server, because localStorage can only be read after hydration and the first paint will use the wrong width.
- Does shadcn/ui include a sidebar component?
- Yes, and it is the strongest free starting point. shadcn/ui ships a Sidebar primitive with a provider, cookie-persisted open state, icon and off-canvas collapse modes, a mobile Sheet, groups, submenus, badges, skeletons and a keyboard toggle bound to cmd or ctrl plus B by default. What it does not give you is the routing and accessibility glue: matching the current path to an item, setting aria-current on it, wrapping the menu in a nav landmark and building the icon-mode flyout are still your code.
- Should a sidebar trap keyboard focus?
- Only when it is an overlay. A docked desktop sidebar is part of the page, so trapping focus inside it would strand keyboard users; a mobile sidebar drawn over the content is a dialog and does need a focus trap, Escape to close, an inert background and focus returned to the trigger. The related bug to watch for is a sidebar collapsed off-canvas that stays in the DOM, because its links remain in the tab order while invisible unless you hide them properly.









