cssgridgen

shadcn Sidebar Generator

Configure it visually, copy the component and the provider shell.

Open in
SidebarTrigger

Click the trigger to see the offcanvas collapse behaviour.

AppSidebar + layout shell
import { Calendar, Home, Inbox, Settings } from "lucide-react"

import {
  Sidebar,
  SidebarHeader,
  SidebarContent,
  SidebarGroup,
  SidebarGroupLabel,
  SidebarGroupContent,
  SidebarMenu,
  SidebarMenuItem,
  SidebarMenuButton,
  SidebarFooter,
  SidebarProvider,
  SidebarRail,
  SidebarTrigger,
} from "@/components/ui/sidebar"

export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarHeader>
        {/* brand or workspace switcher */}
      </SidebarHeader>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>Application</SidebarGroupLabel>
          <SidebarGroupContent>
            <SidebarMenu>
              <SidebarMenuItem>
                <SidebarMenuButton asChild>
                  <a href="#">
                    <Home />
                    <span>Home</span>
                  </a>
                </SidebarMenuButton>
              </SidebarMenuItem>
              <SidebarMenuItem>
                <SidebarMenuButton asChild>
                  <a href="#">
                    <Inbox />
                    <span>Inbox</span>
                  </a>
                </SidebarMenuButton>
              </SidebarMenuItem>
              <SidebarMenuItem>
                <SidebarMenuButton asChild>
                  <a href="#">
                    <Calendar />
                    <span>Calendar</span>
                  </a>
                </SidebarMenuButton>
              </SidebarMenuItem>
            </SidebarMenu>
          </SidebarGroupContent>
        </SidebarGroup>
        <SidebarGroup>
          <SidebarGroupLabel>Settings</SidebarGroupLabel>
          <SidebarGroupContent>
            <SidebarMenu>
              <SidebarMenuItem>
                <SidebarMenuButton asChild>
                  <a href="#">
                    <Settings />
                    <span>General</span>
                  </a>
                </SidebarMenuButton>
              </SidebarMenuItem>
            </SidebarMenu>
          </SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
      <SidebarFooter>
        {/* user menu */}
      </SidebarFooter>
      <SidebarRail />
    </Sidebar>
  )
}

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <SidebarProvider>
      <AppSidebar />
      <main className="flex-1">
        <SidebarTrigger />
        {children}
      </main>
    </SidebarProvider>
  )
}
Free tool

The provider shell is the part that's usually missing

The shadcn sidebar has three props that change its shape entirely, and names — floating, inset, offcanvas — that do not tell you much until you see them. This renders a miniature app shell so each combination is visible, and gives you back both the sidebar and the SidebarProvider layout it has to live inside.

How to use it

  1. Choose the shape

    Side, variant and collapse mode. The preview is a miniature app shell, so you can see what floating and inset actually do rather than guessing from the names.

  2. Toggle the parts

    Header, footer, rail and SidebarInset. Only the parts you enable appear in the imports and the markup.

  3. Build the menu

    Add groups and items with Lucide icons. These become SidebarGroup, SidebarMenu and SidebarMenuButton blocks.

  4. Test the collapse

    Click the trigger in the preview to watch offcanvas, icon and none behave differently.

  5. Copy both pieces

    You get AppSidebar and the layout shell that wraps it — the provider setup is the part most snippets leave out.

Three props do most of the work

side is left or right. variant decides whether the panel is flush (sidebar), detached with its own border (floating), or leaves the main content in a rounded card on the page background (inset). collapsible decides what happens when it closes: slide away entirely (offcanvas), shrink to an icon rail (icon), or never close at all (none).

SidebarProvider has to be outermost

It owns the open state, the keyboard shortcut and the mobile sheet. A SidebarTrigger rendered outside it silently does nothing, which is the single most common thing that goes wrong. The output here always includes the shell so the two cannot drift apart.

Persisting the open state

The component stores its state in a sidebar_state cookie. Read that cookie on the server and pass it as defaultOpen, otherwise the sidebar renders open and snaps shut once the client hydrates.

Laying out the page around it

Everything beside the sidebar is still an ordinary grid problem. For the content area — a dashboard, an app shell, a settings page — use the dashboard layout or the shadcn grid generator.

Frequently asked questions

How do I add a sidebar in shadcn/ui?
Run npx shadcn@latest add sidebar, then wrap your app in SidebarProvider with an <AppSidebar /> and your main content as siblings. The provider owns the open state and the mobile sheet, so nothing works until it is the outermost element.
What is the difference between the sidebar, floating and inset variants?
sidebar is flush against the viewport edge, floating detaches it with a border and rounded corners, and inset pushes the main content into a rounded card so the sidebar sits on the page background. inset expects you to wrap the main area in SidebarInset.
What does collapsible="icon" do?
It shrinks the sidebar to a narrow rail showing only icons instead of hiding it. offcanvas slides it fully off screen, and none makes it permanently open with no trigger. On mobile all variants become a sheet regardless of this setting.
Why is my SidebarTrigger not doing anything?
Almost always one of two things: the trigger is outside SidebarProvider, so it has no context to toggle, or the sidebar is set to collapsible="none", which has no collapsed state to switch to.
How do I put the shadcn sidebar on the right?
Pass side="right" to <Sidebar>. Also swap the DOM order so the sidebar comes after the main content — keyboard users follow the DOM, not the visual position.
How do I keep the sidebar state between page loads?
The component reads and writes a sidebar_state cookie. In Next.js, read it in a server component and pass the value to SidebarProvider as defaultOpen so the first render matches and the sidebar does not flash open.