Build an Admin Dashboard Sidebar with shadcn/ui and Shadcn Space
This article guides developers through building an admin dashboard sidebar using `shadcn/ui` and `Shadcn Space`. It covers project setup, installing pre-built UI blocks, structuring navigation data, handling active states, and styling, demonstrating how to create a feature-rich and accessible sidebar efficiently.

Admin dashboards are a staple in modern web applications, and at their core lies a well-structured sidebar for navigation. Building such a component from scratch can be surprisingly complex, requiring careful consideration for features like collapsible submenus, active state tracking, accessibility, and maintaining a consistent design system across various screen sizes. This tutorial dives into constructing a robust and accessible admin dashboard sidebar using shadcn/ui, a popular collection of React components, and Shadcn Space, a community library providing pre-built UI blocks.
shadcn/ui stands out by not being a traditional component library dependency. Instead, you use a CLI to copy components directly into your project. This approach grants you full ownership over the code, enabling complete customization and ensuring updates to a library don't introduce breaking changes to your components. Its benefits include inherent accessibility, styling with Tailwind CSS, zero lock-in, and compatibility with various React frameworks.
Shadcn Space complements shadcn/ui by offering ready-to-use UI blocks like dashboard layouts and sidebars. These blocks are also installed via the shadcn CLI, becoming part of your codebase without external runtime dependencies. For this tutorial, we leverage the sidebar-06 block from Shadcn Space to quickly get a floating admin sidebar with grouped navigation and collapsible submenus.
Project Setup and Block Installation
To begin, ensure you have Node.js 18+, basic React and TypeScript knowledge, and familiarity with Tailwind CSS. Start by initializing a new Next.js project using the shadcn CLI, which sets up Tailwind CSS, Base UI as the component foundation, and other configurations:
javascript npx shadcn@latest init --preset b0 --base base --template next
After configuring your project, modify your components.json file to include the Shadcn Space registry, allowing you to fetch their blocks:
javascript { "registries": { "@shadcn-space": { "url": "https://shadcnspace.com/r/{name}.json" } } }
With the setup complete, install the sidebar-06 block using your preferred package manager. This command fetches the block and its dependencies (like Sidebar, ScrollArea, Card, Button, Collapsible from shadcn/ui) directly into your project:
javascript npx shadcn@latest add @shadcn-space/sidebar-06
Understanding the Folder Structure
Upon installation, you'll find a clear folder structure:
app/sidebar-06/page.tsx: The main page entry point integrating the sidebar withSidebarProvider.assets/logo/logo.tsx: The application's logo component.components/shadcn-space/blocks/sidebar-06/app-sidebar.tsx: The primary sidebar shell, orchestrating the header, scroll area, navigation data, and promotional card.components/shadcn-space/blocks/sidebar-06/nav-main.tsx: Contains all the navigation rendering logic, including section labels, leaf items, collapsible parents, and active state management.
Building the Page Layout
The app/sidebar-06/page.tsx file defines the overall page layout. It uses SidebarProvider to manage the sidebar's open/closed state via React context. The --sidebar-width CSS custom property, set inline, allows for dynamic width configuration. A SidebarTrigger component in the page header enables users to toggle the sidebar's visibility.
javascript import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import { AppSidebar } from "@/components/shadcn-space/blocks/sidebar-06/app-sidebar";
const Page = () => { return ( <SidebarProvider className="p-4 bg-muted" style={{ "--sidebar-width": "300px" } as React.CSSProperties}> <AppSidebar /> {/* Main content area */} <div className="flex flex-1 flex-col gap-4"> <header className="flex h-14 shrink-0 items-center gap-2 rounded-xl bg-background px-4 shadow-sm"> <SidebarTrigger className="cursor-pointer" /> </header> <main className="flex-1 rounded-xl bg-background" /> </div> </SidebarProvider> ); };
export default Page;
Defining Navigation Data
The navData in app-sidebar.tsx is a flat array of NavItem objects. This simplified structure supports three types of navigation items:
- Section Labels: Marked with
isSection: trueand alabelstring, rendering as uppercase group headings. - Leaf Items: Possessing a
title,icon, andhref, serving as direct navigation links. - Parent Items: Including a
title,icon, and achildrenarray, which render as collapsible triggers for sub-items.
This flat array is straightforward to maintain, with the NavMain component handling the rendering logic based on each item's shape.
javascript export const navData: NavItem[] = [ // Dashboards Section { label: "Dashboards", isSection: true }, { title: "Analytics", icon: PieChart, href: "#" }, { title: "CRM Dashboard", icon: ClipboardList, href: "#" }, // Pages Section { label: "Pages", isSection: true }, { title: "Tables", icon: Table, href: "#" }, { title: "Forms", icon: ClipboardList, href: "#" }, { title: "User Profile", icon: CircleUserRound, href: "#" }, // Apps Section { label: "Apps", isSection: true }, { title: "Notes", icon: Notebook, href: "#" }, { title: "Tickets", icon: Ticket, href: "#" }, { title: "Blogs", icon: Languages, children: [ { title: "Blog Post", href: "#" }, { title: "Blog Detail", href: "#" }, { title: "Blog Edit", href: "#" }, { title: "Blog Create", href: "#" }, { title: "Manage Blogs", href: "#" }, ]}, // Form Elements Section { label: "Form Elements", isSection: true }, { title: "Shadcn Forms", icon: NotepadText, children: [ { title: "Button", href: "#" }, { title: "Input", href: "#" }, { title: "Select", href: "#" }, { title: "Checkbox", href: "#" }, { title: "Radio", href: "#" }, ]}, { title: "Form layouts", icon: AlignStartVertical, children: [ { title: "Forms Horizontal", href: "#" }, { title: "Forms Vertical", href: "#" }, { title: "Forms Validation", href: "#" }, { title: "Forms Examples", href: "#" }, { title: "Forms Wizard", href: "#" }, ]}, ];
Building NavMain and Handling Active States
The nav-main.tsx file orchestrates the rendering of navigation items. It maintains activeParent and activeChild states to track the currently selected items, ensuring the sidebar always has a highlighted selection. These states are passed down to individual NavMainItem components for reactivity.
Section labels are rendered using SidebarGroup and SidebarGroupLabel, with first:pt-0 to align the first section. Collapsible parent items utilize shadcn/ui's Collapsible component. A useEffect hook ensures that when a parent item becomes active, its collapsible state automatically opens. The ChevronRight icon dynamically rotates 90 degrees to indicate the open/closed state of submenus.
Leaf items render as direct links. The render prop on SidebarMenuButton is crucial here, replacing the default button with an <a> tag to maintain correct semantic HTML and accessibility. Clicking a leaf item activates it and resets activeChild to null.
For child items within submenus, the NavMainSubItem function handles rendering. When a child is clicked, it sets both activeChild to itself and activeParent to its parent's title, ensuring both parent and child are visually highlighted. This uses bg-muted and text-foreground for child active states, distinct from the parent's bg-primary and text-primary-foreground, providing clear visual hierarchy.
Styling the Sidebar
The AppSidebar component's render function brings all the visual elements together:
javascript export function AppSidebar() { return ( <Sidebar variant="floating" className="p-4 h-full [&_[data-slot=sidebar-inner]]:h-full"> <div className="flex flex-col gap-6 overflow-hidden"> {/* Header with Logo /} <SidebarHeader className="px-4"> <SidebarMenu> <SidebarMenuItem> <a href="#" className="w-full h-full"> <Logo /> </a> </SidebarMenuItem> </SidebarMenu> </SidebarHeader> {/ Scrollable Navigation Content /} <SidebarContent className="overflow-hidden"> <ScrollArea className="h-[calc(100vh-100px)]"> <div className="px-4"> <NavMain items={navData} /> </div> {/ Promotional Card */} <div className="pt-5 px-4"> <Card className="shadow-none ring-0 bg-secondary px-4 py-6"> <CardContent className="p-0 flex flex-col gap-3 items-center"> <img src="https://images.shadcnspace.com/assets/backgrounds/download-img.png" alt="sidebar-img" width={74} height={74} className="h-20 w-20" /> <div className="flex flex-col gap-4 items-center"> <div> <p className="text-base font-semibold text-card-foreground text-center"> Grab Pro Now </p> <p className="text-sm font-regular text-muted-foreground text-center"> Customize your admin </p> </div> <Button className="w-fit h-9 px-4 py-2 shadow-none cursor-pointer rounded-xl hover:bg-primary/80"> Get Premium </Button> </div> </CardContent> </Card> </div> </ScrollArea> </SidebarContent> </div> </Sidebar> ); }
The variant="floating" property applies a card-like appearance with rounded corners and a subtle drop shadow, visually distinguishing the sidebar. The arbitrary Tailwind selector [&_[data-slot=sidebar-inner]]:h-full ensures the sidebar's internal container correctly fills its available height. For independent scrolling of the navigation list, ScrollArea is used with a calculated height h-[calc(100vh-100px)], accounting for the header and padding to prevent overflow.
Practical Takeaways
Leveraging shadcn/ui and Shadcn Space significantly streamlines the development of complex UI components like admin sidebars. The direct component ownership model provides unparalleled flexibility and control, allowing for deep customization without the typical constraints of external libraries. By combining robust primitives with pre-built blocks, developers can achieve fully functional, accessible, and visually appealing interfaces efficiently, focusing more on application logic rather than intricate UI details.
FAQ
Q: Why does shadcn/ui encourage copying components instead of installing them as a dependency?
A: This approach gives developers full ownership and control over the component's code. You can modify every line, ensuring complete customization and preventing breaking changes from upstream library updates that are outside your control.
Q: What is the purpose of the "use client" directive at the top of app-sidebar.tsx?
A: The "use client" directive is required because components like app-sidebar.tsx utilize React state (through NavMain) and event handlers. These features need to run in the client-side browser environment, rather than being exclusively server-rendered by Next.js.
Q: How does the sidebar manage its open/closed state across different components?
A: The sidebar's open/closed state is managed by SidebarProvider, which wraps the entire page layout. This provider uses React context to pass the state down to child components like SidebarTrigger and AppSidebar, allowing them to read and update the sidebar's visibility as needed.
Related articles
Why AI hasn't Replaced Human Expertise in Your SaaS Stack
As software developers, we've all seen the headlines and the seductive promise: AI would become the ultimate answer engine, allowing us to code with minimal human interaction. The vision of prompting our way to perfect
Community-First AI Cloud: Scaling GPUs Without VC Drama
Many of us developers dream of building a groundbreaking product, perhaps even a startup. The conventional wisdom often points to seeking venture capital (VC) funding as a prerequisite for scale. But what if there was
Trump Supporters Debate: Is He the Antichrist
Staunch Trump supporters are now publicly questioning if he is the Antichrist, a dramatic shift from their previous perception of him as "God's chosen president." This re-evaluation was primarily triggered by an AI-generated image of Trump resembling Jesus Christ, alongside his administration's actions regarding the Iran war and recent criticism of the Vatican. High-profile conservative figures have openly expressed concern, calling the behavior blasphemous or indicative of an "Antichrist spirit." This growing schism could have significant political implications for Trump and the Republican Party, particularly among Catholic voters.
policy: Can AI be a ‘child of God’? Inside Anthropic’s meeting with
AI firm Anthropic, valued at $380 billion, recently met with Christian leaders in San Francisco for guidance on building a moral chatbot, an unprecedented move in Silicon Valley. This rare consultation highlights the complex ethical questions surrounding advanced AI, including its potential spiritual dimensions.
Build a Secure AI PR Reviewer with Claude, GitHub Actions, and JS
This article details how to build a secure AI-powered pull request reviewer using JavaScript, Claude, and GitHub Actions. It focuses on critical security aspects like sanitizing untrusted diff input, validating probabilistic LLM output with Zod, and employing fail-closed mechanisms to ensure robustness and prevent vulnerabilities.
Reddit Data Demand: Grand Jury Summons Raises Privacy Alarm
Ars Technica reports on the Trump administration's escalating efforts to unmask an ICE critic on Reddit, moving from a standard summons to a secret grand jury subpoena, sparking significant privacy and free speech concerns for online users and platforms.





