<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ baseui - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ baseui - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 31 May 2026 09:38:30 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/baseui/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Admin Dashboard Sidebar with shadcn/ui and Base UI ]]>
                </title>
                <description>
                    <![CDATA[ Admin dashboards are one of the most common real-world UI components you will build as a React developer. At the heart of nearly every dashboard is a sidebar, a persistent navigation panel that organi ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-admin-dashboard-sidebar-with-shadcn-ui-and-base-ui/</link>
                <guid isPermaLink="false">69de6a6491716f3cfb542305</guid>
                
                    <category>
                        <![CDATA[ shadcn ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ baseui ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vaibhav Gupta ]]>
                </dc:creator>
                <pubDate>Tue, 14 Apr 2026 16:25:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/3ce152b1-9a34-4c72-85f0-cabf7d4f3460.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Admin dashboards are one of the most common real-world UI components you will build as a React developer. At the heart of nearly every dashboard is a sidebar, a persistent navigation panel that organizes pages, tools, and features into a clean, scannable structure.</p>
<p>Building a sidebar from scratch involves much more than an <code>&lt;nav&gt;</code> element. You need collapsible submenus, active state tracking across parent and child items, accessible keyboard navigation, a scroll area for long nav lists, and a consistent design system that holds together across screen sizes.</p>
<p>In this tutorial, you'll learn how to build a fully functional, accessible admin dashboard sidebar using shadcn/ui, a collection of beautifully designed, accessible React components, and a pre-built community block from Shadcn Space, which extends shadcn/ui with ready-to-use dashboard UI patterns.</p>
<p>By the end of this tutorial, you'll have a working sidebar that includes:</p>
<ul>
<li><p>Grouped navigation sections with uppercase labels</p>
</li>
<li><p>Collapsible parent menu items with child links</p>
</li>
<li><p>Active state tracking across both parent and child items</p>
</li>
<li><p>A floating sidebar with an independent scroll area</p>
</li>
<li><p>A promotional card pinned inside the sidebar footer</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-you-will-build">What You Will Build</a></p>
</li>
<li><p><a href="#heading-why-shadcnui">Why shadcn/ui?</a></p>
</li>
<li><p><a href="#heading-what-is-shadcn-space">What is Shadcn Space?</a></p>
</li>
<li><p><a href="#heading-how-to-install-the-sidebar-block">How to Install the Sidebar Block</a></p>
</li>
<li><p><a href="#heading-how-to-understand-the-folder-structure">How to Understand the Folder Structure</a></p>
</li>
<li><p><a href="#heading-how-to-build-the-page-layout">How to Build the Page Layout</a></p>
</li>
<li><p><a href="#heading-how-to-build-the-appsidebar-component">How to Build the AppSidebar Component</a></p>
</li>
<li><p><a href="#heading-how-to-define-the-navigation-data">How to Define the Navigation Data</a></p>
</li>
<li><p><a href="#heading-how-to-build-the-navmain-component">How to Build the NavMain Component</a></p>
</li>
<li><p><a href="#heading-how-to-handle-active-states-and-collapsible-menus">How to Handle Active States and Collapsible Menus</a></p>
</li>
<li><p><a href="#heading-how-to-style-the-sidebar">How to Style the Sidebar</a></p>
</li>
<li><p><a href="#heading-live-preview">Live Preview</a></p>
</li>
<li><p><a href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before you start, make sure you have the following:</p>
<ul>
<li><p>Node.js 18+ installed on your machine</p>
</li>
<li><p>Basic knowledge of React and TypeScript</p>
</li>
<li><p>Familiarity with Tailwind CSS utility classes</p>
</li>
<li><p>A package manager installed (npm, pnpm, yarn, or bun)</p>
</li>
</ul>
<p>You don't need prior experience with shadcn/ui, but it helps to have read through <a href="https://ui.shadcn.com/docs">the official docs</a> at least once.</p>
<h2 id="heading-what-you-will-build"><strong>What You Will Build</strong></h2>
<p>In this article, you'll build a fully functional admin dashboard sidebar with the following features:</p>
<ol>
<li><p><strong>Floating sidebar shell</strong>: a card-style sidebar with rounded corners, a drop shadow, and a configurable width</p>
</li>
<li><p><strong>Grouped navigation</strong>: navigation items organized under section labels like Dashboards, Pages, Apps, and Form Elements</p>
</li>
<li><p><strong>Collapsible submenus</strong>: parent items like Blogs and Shadcn Forms that expand on click to reveal child links</p>
</li>
<li><p><strong>Active state tracking</strong>: visual highlighting of the selected parent and child item at all times</p>
</li>
<li><p><strong>Sidebar toggle</strong>: a trigger button in the page header that opens and closes the sidebar</p>
</li>
<li><p><strong>Promotional card</strong>: a "Get Premium" card at the bottom of the sidebar scroll area</p>
</li>
</ol>
<h2 id="heading-why-shadcnui"><strong>Why shadcn/ui?</strong></h2>
<p><a href="https://ui.shadcn.com/"><strong>shadcn/ui</strong></a> is a collection of beautifully designed, accessible React components built on top of Radix UI and styled with Tailwind CSS.</p>
<p>Instead of installing a traditional component library as a dependency, you copy components directly into your project using a CLI. This gives you full ownership of the code structure and styling. You can read every line, change anything, and the components never break because of a library update you didn't control.</p>
<p>Some key benefits of shadcn/ui include:</p>
<ul>
<li><p>Accessible by default, built on Radix and Base UI primitives</p>
</li>
<li><p>Fully styled with Tailwind CSS utility classes</p>
</li>
<li><p>Zero lock-in: the code lives in your project, not inside <code>node_modules</code></p>
</li>
<li><p>Works with Next.js, React, Astro, Vite, and other frameworks</p>
</li>
<li><p>A growing ecosystem of community-built blocks and registries</p>
</li>
</ul>
<p>The <code>Sidebar, Collapsible, ScrollArea, Card, and Button</code> Components you'll use in this tutorial all come from shadcn/ui.</p>
<h2 id="heading-what-is-shadcn-space"><strong>What is Shadcn Space?</strong></h2>
<p><strong>Shadcn Space</strong> is an open-source library of pre-built <a href="https://shadcnspace.com/blocks"><strong>UI blocks</strong></a> built on top of shadcn/ui. It provides ready-to-use dashboard layouts, sidebars, tables, cards, and other common admin UI patterns so you don't have to assemble them from individual primitives every time.</p>
<p>Each block in Shadcn Space is installable directly into your project using the shadcn CLI. Once installed, the code is yours: you can read it, extend it, and adapt it to your design system without any runtime dependency on Shadcn Space itself.</p>
<p>For this tutorial, you'll use the <code>sidebar-06</code> block (it’s free to use), which is a floating admin sidebar with grouped navigation, collapsible submenus, and an integrated scroll area.</p>
<p>Shadcn Space also provides a companion <a href="https://www.figma.com/community/file/1597967874273587400/shadcn-space-figma-ui-kit"><strong>Figma UI Kit</strong></a> that matches the design system used in the blocks, which is useful if you do design work alongside development.</p>
<p>You can explore the full block library and the getting-started documentation in the <a href="https://shadcnspace.com/docs/getting-started/blocks"><strong>official Shadcn Space docs</strong></a>.</p>
<h3 id="heading-how-to-set-up-the-project">How to Set Up the Project</h3>
<p>Start by creating a new Next.js project if you don't already have one:</p>
<pre><code class="language-javascript">npx shadcn@latest init --preset b0 --base base --template next
</code></pre>
<p>This command:</p>
<ul>
<li><p>Creates a Next.js project</p>
</li>
<li><p>Configures Tailwind CSS</p>
</li>
<li><p>Sets up Base UI as the component foundation</p>
</li>
<li><p>Uses Nova style preset</p>
</li>
<li><p>Configures Lucide icons</p>
</li>
<li><p>Uses Inter font</p>
</li>
<li><p>Applies neutral theme tokens</p>
</li>
</ul>
<p>Follow the prompts to configure your base color, CSS variables, and component output directory. This sets up the <code>components/ui</code> directory and the required Tailwind configuration that all shadcn/ui components depend on.</p>
<p>Once the initialization is complete, your <code>components.json</code> project will be created at the root of your project. This file tells the shadcn CLI where to place components, what path aliases you're using, and which styling configuration to follow.</p>
<p>Add this in <code>components.json</code>:</p>
<pre><code class="language-javascript">{
  "registries": {
    "@shadcn-space": {
      "url": "https://shadcnspace.com/r/{name}.json",
    }
  }
}
</code></pre>
<h2 id="heading-how-to-install-the-sidebar-block"><strong>How to Install the Sidebar Block</strong></h2>
<p>With shadcn/ui initialized, you can now pull in the <code>sidebar-06</code> block from Shadcn Space. While Shadcn Space provides components for both Radix UI and Base UI, this tutorial uses the Base UI version. Run one of the following commands depending on your package manager:</p>
<p><strong>npm:</strong></p>
<pre><code class="language-javascript">npx shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p><strong>pnpm:</strong></p>
<pre><code class="language-javascript">pnpm dlx shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p><strong>yarn:</strong></p>
<pre><code class="language-javascript">yarn dlx shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p><strong>bun:</strong></p>
<pre><code class="language-javascript">bunx --bun shadcn@latest add @shadcn-space/sidebar-06
</code></pre>
<p>This command fetches the block from the Shadcn Space registry and scaffolds all the required component files into your project automatically. It also installs any shadcn/ui primitives the block depends on (such as <code>Sidebar, ScrollArea, Card, Button,</code> and <code>Collapsible</code>) if they aren't already present in your components/ui directory.</p>
<p>You can preview the live block and find the installation command on their <a href="https://shadcnspace.com/blocks/dashboard-ui/sidebars"><strong>shadcn sidebar</strong></a> page.</p>
<h2 id="heading-how-to-understand-the-folder-structure"><strong>How to Understand the Folder Structure</strong></h2>
<p>After installation, your project will contain the following new files:</p>
<pre><code class="language-javascript">app/
  sidebar-06/
    page.tsx                  ← Route entry point
assets/
  logo/
    logo.tsx                  ← Logo component
components/
  shadcn-space/
    blocks/
      sidebar-06/
        app-sidebar.tsx       ← Main sidebar shell
        nav-main.tsx          ← Navigation logic and rendering
</code></pre>
<p>Each file has a clearly defined responsibility:</p>
<ul>
<li><p><code>app/sidebar-06/page.tsx</code>: the route entry point that wires the sidebar into a page layout using SidebarProvider</p>
</li>
<li><p><code>assets/logo/logo.tsx</code>: the logo component rendered in the sidebar header</p>
</li>
<li><p><code>components/shadcn-space/blocks/sidebar-06/app-sidebar.tsx</code>: the main sidebar shell, including the header, scroll area, nav data, and promotional card</p>
</li>
<li><p><code>components/shadcn-space/blocks/sidebar-06/nav-main.tsx</code>: all navigation rendering logic, including section labels, leaf items, collapsible parents, and active state management</p>
</li>
</ul>
<p>You'll work through each of these files in detail in the sections below.</p>
<h2 id="heading-how-to-build-the-page-layout"><strong>How to Build the Page Layout</strong></h2>
<p>Open <code>app/sidebar-06/page.tsx</code>. This file is the entry point for your dashboard page. It uses <code>SidebarProvider</code> to establish sidebar context across the page, and <code>SidebarTrigger</code> to render a toggle button inside the header.</p>
<pre><code class="language-javascript">import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/shadcn-space/blocks/sidebar-06/app-sidebar";

const Page = () =&gt; {
  return (
    &lt;SidebarProvider
      className="p-4 bg-muted"
      style={{ "--sidebar-width": "300px" } as React.CSSProperties}
    &gt;
      &lt;AppSidebar /&gt;

      {/* Main content area */}
      &lt;div className="flex flex-1 flex-col gap-4"&gt;
        &lt;header className="flex h-14 shrink-0 items-center gap-2 rounded-xl bg-background px-4 shadow-sm"&gt;
          &lt;SidebarTrigger className="cursor-pointer" /&gt;
        &lt;/header&gt;
        &lt;main className="flex-1 rounded-xl bg-background" /&gt;
      &lt;/div&gt;
    &lt;/SidebarProvider&gt;
  );
};

export default Page;
</code></pre>
<p>Let's break down the key parts of this layout:</p>
<p><strong>SidebarProvider</strong> wraps everything on the page. It manages the sidebar's open/closed state and passes it down to child components via React context. Any component that needs to read or change the sidebar state, including <code>SidebarTrigger</code> and <code>AppSidebar</code>, must be a descendant of <code>SidebarProvider</code>.</p>
<p><strong>The</strong> <code>--sidebar-width</code> <strong>CSS custom property</strong> controls the rendered width of the sidebar. It's set inline using a type assertion (<code>as React.CSSProperties</code>) because TypeScript doesn't know about this custom property by default. Setting it here rather than in a CSS file keeps the width configurable on a per-page basis.</p>
<p><code>SidebarTrigger</code> is a toggle button component that reads the sidebar open/closed state from the nearest <code>SidebarProvider</code> context and flips it on click. It renders in the header so users always have access to the toggle regardless of scroll position.</p>
<p><code>bg-muted</code> on <code>SidebarProvider</code> creates the light gray outer background that makes the floating sidebar card visually stand out from the page.</p>
<h2 id="heading-how-to-build-the-appsidebar-component"><strong>How to Build the AppSidebar Component</strong></h2>
<p>Open <code>components/shadcn-space/blocks/sidebar-06/app-sidebar.tsx</code>. This component is the main sidebar shell. It composes shadcn/ui's <code>Sidebar</code>, <code>SidebarHeader</code>, and <code>SidebarContent</code> layout primitives and wraps the scrollable navigation area in a <code>ScrollArea</code> component to handle overflow independently.</p>
<pre><code class="language-javascript">"use client";

import {
  Sidebar,
  SidebarContent,
  SidebarHeader,
  SidebarMenu,
  SidebarMenuItem,
} from "@/components/ui/sidebar";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import Logo from "@/assets/logo/logo";
import { NavItem, NavMain } from "@/components/shadcn-space/blocks/sidebar-06/nav-main";
import {
  AlignStartVertical,
  PieChart,
  CircleUserRound,
  ClipboardList,
  Notebook,
  NotepadText,
  Table,
  Languages,
  Ticket,
} from "lucide-react";
</code></pre>
<p>The <code>"use client"</code> directive at the top is required because this component uses React state (through <code>NavMain</code>) and event handlers, both of which require the component to run in the browser rather than being server-rendered by Next.js.</p>
<h2 id="heading-how-to-define-the-navigation-data"><strong>How to Define the Navigation Data</strong></h2>
<p>Inside <code>app-sidebar.tsx</code>the navigation structure is defined as a flat array of <code>NavItem</code> objects. Each item belongs to one of three categories:</p>
<ol>
<li><p><strong>A section label</strong> marked with <code>isSection: true</code> and a <code>label</code> string. Renders as an uppercase group heading.</p>
</li>
<li><p><strong>A leaf item</strong> has a <code>title, icon</code>, and <code>href</code>, but no <code>children</code>. Renders as a direct navigation link.</p>
</li>
<li><p><strong>A parent item</strong> has a <code>title, icon</code>, and a <code>children</code> array of sub-items. Renders as a collapsible trigger.</p>
</li>
</ol>
<pre><code class="language-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: "#" },
    ],
  },
];
</code></pre>
<p>This flat array approach is intentionally simple to maintain. You don't need a nested tree structure because the <code>NavMain</code> component handles the rendering logic for each item type by inspecting each item's shape. Adding a new section, item, or submenu is as straightforward as appending a new object to the array.</p>
<h2 id="heading-how-to-build-the-navmain-component"><strong>How to Build the NavMain Component</strong></h2>
<p>Open <code>components/shadcn-space/blocks/sidebar-06/nav-main.tsx</code>. This file contains all the navigation rendering logic. Start with the type definition and the top-level <code>NavMain</code> function:</p>
<pre><code class="language-javascript">"use client";

import * as React from "react";
import { ChevronRight, LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import {
  Collapsible,
  CollapsibleTrigger,
  CollapsibleContent,
} from "@/components/ui/collapsible";
import {
  SidebarGroup,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSub,
  SidebarMenuSubItem,
  SidebarMenuSubButton,
} from "@/components/ui/sidebar";

export type NavItem = {
  label?: string;
  isSection?: boolean;
  title?: string;
  icon?: LucideIcon;
  href?: string;
  children?: NavItem[];
};

export function NavMain({ items }: { items: NavItem[] }) {
  const [activeParent, setActiveParent] = React.useState&lt;string | null&gt;(
    items.find((i) =&gt; !i.isSection)?.title || null
  );
  const [activeChild, setActiveChild] = React.useState&lt;string | null&gt;(null);

  return (
    &lt;&gt;
      {items.map((item, index) =&gt; (
        &lt;NavMainItem
          key={item.title || item.label || index}
          item={item}
          activeParent={activeParent}
          setActiveParent={setActiveParent}
          activeChild={activeChild}
          setActiveChild={setActiveChild}
        /&gt;
      ))}
    &lt;/&gt;
  );
}
</code></pre>
<p><code>activeParent</code> tracks which top-level nav item is currently selected. It initializes to the title of the first non-section item, so the sidebar always has a selection on first render, and you never show the sidebar with nothing highlighted. <code>activeChild</code> tracks which sub-item inside a collapsible menu is selected.</p>
<p>Both state values are passed down as props to each <code>NavMainItem</code>, so every item in the list can read the current selection and trigger updates to it.</p>
<h2 id="heading-how-to-handle-active-states-and-collapsible-menus"><strong>How to Handle Active States and Collapsible Menus</strong></h2>
<p>The <code>NavMainItem</code> function branches into one of three rendering paths based on the shape of the incoming item.</p>
<h3 id="heading-how-to-render-section-labels">How to Render Section Labels</h3>
<pre><code class="language-javascript">if (item.isSection &amp;&amp; item.label) {
  return (
    &lt;SidebarGroup className="p-0 pt-5 first:pt-0"&gt;
      &lt;SidebarGroupLabel className="p-0 text-xs font-medium uppercase text-sidebar-foreground"&gt;
        {item.label}
      &lt;/SidebarGroupLabel&gt;
    &lt;/SidebarGroup&gt;
  );
}
</code></pre>
<p>Section labels use <code>first:pt-0</code> to remove the top padding from the very first section, so the nav starts flush with the header.</p>
<h3 id="heading-how-to-render-collapsible-parent-items">How to Render Collapsible Parent Items</h3>
<pre><code class="language-javascript">if (hasChildren &amp;&amp; item.title) {
  return (
    &lt;SidebarGroup className="p-0"&gt;
      &lt;SidebarMenu&gt;
        &lt;Collapsible open={isOpen} onOpenChange={setIsOpen}&gt;
          &lt;SidebarMenuItem&gt;
            &lt;CollapsibleTrigger
              className="w-full"
              render={
                &lt;SidebarMenuButton
                  id={`nav-main-trigger-${item.title.toLowerCase().replace(/\s+/g, '-')}`}
                  tooltip={item.title}
                  isActive={isParentActive}
                  onClick={() =&gt; setActiveParent(item.title!)}
                  className={cn(
                    "rounded-md text-sm font-medium px-3 py-2 h-9 transition-colors cursor-pointer",
                    isParentActive ? "bg-primary! text-primary-foreground!" : ""
                  )}
                &gt;
                  {item.icon &amp;&amp; &lt;item.icon size={16} /&gt;}
                  &lt;span&gt;{item.title}&lt;/span&gt;
                  &lt;ChevronRight
                    className={cn(
                      "ml-auto transition-transform duration-200",
                      isOpen &amp;&amp; "rotate-90"
                    )}
                  /&gt;
                &lt;/SidebarMenuButton&gt;
              }
            /&gt;
            &lt;CollapsibleContent&gt;
              &lt;SidebarMenuSub className="me-0 pe-0"&gt;
                {item.children!.map((child, index) =&gt; (
                  &lt;NavMainSubItem
                    key={child.title || index}
                    item={child}
                    activeParent={activeParent}
                    setActiveParent={setActiveParent}
                    activeChild={activeChild}
                    setActiveChild={setActiveChild}
                    parentTitle={item.title}
                  /&gt;
                ))}
              &lt;/SidebarMenuSub&gt;
            &lt;/CollapsibleContent&gt;
          &lt;/SidebarMenuItem&gt;
        &lt;/Collapsible&gt;
      &lt;/SidebarMenu&gt;
    &lt;/SidebarGroup&gt;
  );
}
</code></pre>
<p>A <code>useEffect</code> inside <code>NavMainItem</code> syncs the local <code>isOpen</code> state with <code>activeParent</code> so that when a different parent is activated, the previously open collapsible stays open until the user explicitly closes it:</p>
<pre><code class="language-javascript">React.useEffect(() =&gt; {
  if (isParentActive) {
    setIsOpen(true);
  }
}, [isParentActive]);
</code></pre>
<p>The <code>ChevronRight</code> icon rotates 90 degrees when the submenu is open, using a Tailwind transition class:</p>
<pre><code class="language-javascript">&lt;ChevronRight
  className={cn(
    "ml-auto transition-transform duration-200",
    isOpen &amp;&amp; "rotate-90"
  )}
/&gt;
</code></pre>
<h3 id="heading-how-to-render-leaf-items">How to Render Leaf Items</h3>
<pre><code class="language-javascript">if (item.title) {
  return (
    &lt;SidebarGroup className="p-0"&gt;
      &lt;SidebarMenu&gt;
        &lt;SidebarMenuItem&gt;
          &lt;SidebarMenuButton
            id={`nav-main-button-${item.title.toLowerCase().replace(/\s+/g, '-')}`}
            tooltip={item.title}
            isActive={isParentActive}
            onClick={() =&gt; {
              setActiveParent(item.title!);
              setActiveChild(null);
            }}
            className={cn(
              "rounded-md text-sm font-medium px-3 py-2 h-9 transition-colors cursor-pointer",
              isParentActive ? "bg-primary! text-primary-foreground!" : ""
            )}
            render={&lt;a href={item.href} /&gt;}
          &gt;
            {item.icon &amp;&amp; &lt;item.icon /&gt;}
            {item.title}
          &lt;/SidebarMenuButton&gt;
        &lt;/SidebarMenuItem&gt;
      &lt;/SidebarMenu&gt;
    &lt;/SidebarGroup&gt;
  );
}
</code></pre>
<p>The <code>render</code> prop on <code>SidebarMenuButton</code> replaces the default button element with an <code>&lt;a&gt;</code> tag. This preserves correct anchor link semantics and accessibility while keeping the button's visual styling. When a leaf item is clicked, <code>activeChild</code> is reset to <code>null</code> since there is no child to track.</p>
<h3 id="heading-how-to-render-child-items-in-a-submenu">How to Render Child Items in a Submenu</h3>
<p>The <code>NavMainSubItem</code> function handles sub-items inside a collapsible. When a child is clicked, it sets both <code>activeChild</code> to itself and <code>activeParent</code> to its parent's title so the parent item remains visually highlighted:</p>
<pre><code class="language-javascript">if (item.title) {
  return (
    &lt;SidebarMenuSubItem className="w-full"&gt;
      &lt;SidebarMenuSubButton
        id={`nav-sub-button-${item.title.toLowerCase().replace(/\s+/g, '-')}`}
        className={cn(
          "w-full rounded-md transition-colors",
          activeChild === item.title ? "bg-muted! text-foreground!" : ""
        )}
        isActive={activeChild === item.title}
        onClick={() =&gt; {
          setActiveParent(parentTitle || "");
          setActiveChild(item.title!);
        }}
        render={&lt;a href={item.href}&gt;{item.title}&lt;/a&gt;}
      /&gt;
    &lt;/SidebarMenuSubItem&gt;
  );
}
</code></pre>
<p>The child uses a different active style (<code>bg-muted</code> with <code>text-foreground</code>) compared to the parent (<code>bg-primary</code> with <code>text-primary-foreground</code>). This visual distinction makes it easy to see both which section you are in and which specific page is currently active.</p>
<p>Sub-items also support nesting. If a child item itself has a <code>children</code> array, <code>NavMainSubItem</code> renders another <code>Collapsible</code> with a nested <code>SidebarMenuSub</code>, allowing you to build multi-level navigation trees without any changes to the data structure.</p>
<h2 id="heading-how-to-style-the-sidebar"><strong>How to Style the Sidebar</strong></h2>
<p>The full <code>AppSidebar</code> render function puts all of the pieces together:</p>
<pre><code class="language-javascript">export function AppSidebar() {
  return (
    &lt;Sidebar variant="floating" className="p-4 h-full [&amp;_[data-slot=sidebar-inner]]:h-full"&gt;
      &lt;div className="flex flex-col gap-6 overflow-hidden"&gt;

        {/* Header with Logo */}
        &lt;SidebarHeader className="px-4"&gt;
          &lt;SidebarMenu&gt;
            &lt;SidebarMenuItem&gt;
              &lt;a href="#" className="w-full h-full"&gt;
                &lt;Logo /&gt;
              &lt;/a&gt;
            &lt;/SidebarMenuItem&gt;
          &lt;/SidebarMenu&gt;
        &lt;/SidebarHeader&gt;

        {/* Scrollable Navigation Content */}
        &lt;SidebarContent className="overflow-hidden"&gt;
          &lt;ScrollArea className="h-[calc(100vh-100px)]"&gt;
            &lt;div className="px-4"&gt;
              &lt;NavMain items={navData} /&gt;
            &lt;/div&gt;

            {/* Promotional Card */}
            &lt;div className="pt-5 px-4"&gt;
              &lt;Card className="shadow-none ring-0 bg-secondary px-4 py-6"&gt;
                &lt;CardContent className="p-0 flex flex-col gap-3 items-center"&gt;
                  &lt;img
                    src="https://images.shadcnspace.com/assets/backgrounds/download-img.png"
                    alt="sidebar-img"
                    width={74}
                    height={74}
                    className="h-20 w-20"
                  /&gt;
                  &lt;div className="flex flex-col gap-4 items-center"&gt;
                    &lt;div&gt;
                      &lt;p className="text-base font-semibold text-card-foreground text-center"&gt;
                        Grab Pro Now
                      &lt;/p&gt;
                      &lt;p className="text-sm font-regular text-muted-foreground text-center"&gt;
                        Customize your admin
                      &lt;/p&gt;
                    &lt;/div&gt;
                    &lt;Button className="w-fit h-9 px-4 py-2 shadow-none cursor-pointer rounded-xl hover:bg-primary/80"&gt;
                      Get Premium
                    &lt;/Button&gt;
                  &lt;/div&gt;
                &lt;/CardContent&gt;
              &lt;/Card&gt;
            &lt;/div&gt;
          &lt;/ScrollArea&gt;
        &lt;/SidebarContent&gt;

      &lt;/div&gt;
    &lt;/Sidebar&gt;
  );
}
</code></pre>
<p>Let's walk through the key styling decisions:</p>
<p><code>variant="floating"</code> gives the sidebar a card-like appearance with rounded corners and a subtle drop shadow. It visually lifts the sidebar off the background rather than making it flush with the page edge like a standard sidebar would.</p>
<p><code>[&amp;_[data-slot=sidebar-inner]]:h-full</code> is an arbitrary Tailwind variant selector that targets shadcn/ui's internal sidebar slot element. Without this, the sidebar inner container doesn't fill the full available height, which breaks the layout. The <code>data-slot</code> attribute is how shadcn/ui identifies internal sub-elements of compound components.</p>
<p><code>h-[calc(100vh-100px)]</code> on <code>ScrollArea</code> makes the navigation list independently scrollable. The 100px offset accounts for the sidebar header and padding, so the scroll area doesn't overflow the viewport. The rest of the page layout remains static while the nav scrolls.</p>
<p>The <code>bg-secondary</code> card at the bottom of the scroll area is a common admin dashboard pattern, a soft prompt for an upgrade or onboarding action that lives passively in the sidebar without blocking navigation.</p>
<p>For more details on the Sidebar component's API, variants, and configuration options, refer to the official shadcn/ui sidebar docs.</p>
<h2 id="heading-live-preview"><strong>Live Preview</strong></h2>
<img src="https://cdn.hashnode.com/uploads/covers/68b53a3d851476bd2ce87f12/f1538441-fa73-4eb0-af91-04f5bf4fab08.png" alt="f1538441-fa73-4eb0-af91-04f5bf4fab08" style="display:block;margin:0 auto" width="1440" height="892" loading="lazy">

<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>Congratulations! You have now built a complete, production-ready admin dashboard sidebar using shadcn/ui and a community block from Shadcn Space.</p>
<p>Here is a recap of everything you covered:</p>
<ul>
<li><p>Setting up a Next.js project with shadcn/ui initialized and a pre-built sidebar block installed from Shadcn Space</p>
</li>
<li><p>Using <code>SidebarProvider</code> and <code>SidebarTrigger</code> to manage the sidebar open/closed state across a page layout through React context</p>
</li>
<li><p>Defining navigation data as a flat array of typed <code>NavItem</code> objects covering section labels, leaf items, and collapsible parent items</p>
</li>
<li><p>Rendering all three item types from a single <code>navData</code> source in the <code>NavMain</code> and <code>NavMainItem</code> components</p>
</li>
<li><p>Tracking <code>activeParent</code> and <code>activeChild</code> state in a single location and passing them as props so every item can read and update the shared selection state</p>
</li>
<li><p>Using <code>Collapsible</code> with a <code>useEffect</code> sync to keep parent items open when they are active, and animate the chevron icon on expand and collapse</p>
</li>
<li><p>Applying the <code>floating</code> variant, an arbitrary Tailwind slot selector, and <code>ScrollArea</code> with a calculated height to produce a polished, production-appropriate sidebar layout</p>
</li>
</ul>
<p>This pattern scales well beyond what you built here. You can extend <code>NavItem</code> with additional fields like badge counts, permission flags, or external link indicators. You can swap in real <code>href</code> values and connect <code>activeParent</code> and <code>activeChild</code> to your router so the selection always reflects the current URL. You can also add more sections to <code>navData</code> without touching any rendering logic.</p>
<p>For a quick checkout, we have used the Shadcn Space free Shadcn dashboard block in this <a href="https://shadcnspace.com/blocks/dashboard-ui/dashboard-shell"><strong>dashboard shell</strong></a>.</p>
<p>If you want to explore more pre-built admin UI blocks, components, and templates built on top of shadcn/ui, you can browse the full library at <a href="https://shadcnspace.com/"><strong>Shadcn Space</strong></a>.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<ul>
<li><p><a href="https://shadcnspace.com/blocks"><strong>Shadcn UI Blocks</strong></a></p>
</li>
<li><p><a href="https://shadcnspace.com/components"><strong>Shadcn UI Components</strong></a></p>
</li>
<li><p><a href="https://shadcnspace.com/docs/getting-started/blocks"><strong>Shadcn Space Getting Started Docs</strong></a></p>
</li>
<li><p><a href="https://www.figma.com/community/file/1597967874273587400/shadcn-space-figma-ui-kit"><strong>Figma UI Kit Design System</strong></a></p>
</li>
<li><p><a href="https://ui.shadcn.com/docs/components/sidebar"><strong>shadcn/ui Sidebar Docs</strong></a></p>
</li>
<li><p><a href="https://base-ui.com/"><strong>Base UI</strong></a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
