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
Anthropic's Ties to Trump Admin Warm Amid Pentagon Rift
Anthropic's ties with the Trump administration are thawing, marked by a high-level meeting between CEO Dario Amodei and White House officials. This occurs despite an ongoing legal battle with the Pentagon, which labeled Anthropic a "supply-chain risk" over ethical disagreements on AI use.
Boosting LLM Accuracy: Building a Context Hub Relevance Engine
Context Hub (`chub`) addresses LLM limitations by providing coding agents with curated, versioned documentation and skills via a CLI, augmented by local annotations and maintainer feedback. This article explores `chub`'s workflow and content model, then demonstrates building a companion relevance engine. This engine uses an additive reranking layer with extracted signals to significantly improve search accuracy for shorthand queries without altering `chub`'s core design.
AI Data Centers: Denials vs. Satellites - The Unfolding Build-Out Saga
Quick Verdict: AI's Infrastructure Hit by Reality Checks Independent analysis, backed by satellite imagery and on-the-ground reports, suggests that nearly half of crucial AI data center projects in the U.S. are facing
Sovereign AI: Orchestrating National AI Capabilities with Kubernetes
The concept of sovereign AI aims to prevent any country from being left behind in the AI revolution by ensuring national control over AI data, models, and infrastructure. Key challenges include significant infrastructure constraints like power, cooling, and scarce hardware, which lead to regional disparities. This vision relies on extending Kubernetes for robust orchestration and integrating the PyTorch Stack for flexible AI development, enabling countries to build independent and secure AI ecosystems.
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






