A sidebar is the backbone of any admin dashboard. These 4 examples cover the most common patterns — basic nav, collapsible icon mode, dark theme and mobile-responsive with Sheet drawer.
Installation
npx shadcn@latest add button sheet tooltip
npm install lucide-react
Key Patterns
| Pattern | Use case |
|---|---|
| Basic sidebar | Simple dashboards |
| Collapsible | Space-constrained layouts |
| Dark sidebar | Admin panels, developer tools |
| Sheet on mobile | Any responsive dashboard |
1. Basic React Sidebar
Clean sidebar with nav links, active state detection and user footer.
import { useState } from "react"
import { LayoutDashboard, Package, Users, BarChart3, Settings, LogOut } from "lucide-react"
const navItems = [
{ label: "Dashboard", icon: LayoutDashboard, href: "/dashboard", active: true },
{ label: "Templates", icon: Package, href: "/templates", badge: "12" },
{ label: "Customers", icon: Users, href: "/customers" },
{ label: "Analytics", icon: BarChart3, href: "/analytics" },
{ label: "Settings", icon: Settings, href: "/settings" },
]
export default function BasicSidebar() {
const [active, setActive] = useState("Dashboard")
return (
<div className="flex h-screen bg-gray-50">
{/* Sidebar */}
<aside className="w-60 bg-white border-r flex flex-col">
{/* Brand */}
<div className="px-4 py-4 border-b">
<div className="flex items-center gap-2">
<div
className="w-7 h-7 rounded flex items-center justify-center text-white text-xs font-bold flex-shrink-0"
style={{ background: "#fd4766" }}
>
AP
</div>
<span className="font-bold" style={{ color: "#fd4766" }}>AdminPanel</span>
</div>
</div>
{/* Nav */}
<nav className="flex-1 py-3 px-2">
<p className="text-xs font-semibold text-gray-400 uppercase tracking-wider px-2 mb-2">Main</p>
{navItems.map((item) => {
const Icon = item.icon
const isActive = active === item.label
return (
<a
key={item.label}
href={item.href}
onClick={(e) => { e.preventDefault(); setActive(item.label) }}
className="flex items-center gap-3 px-3 py-2 rounded-md mb-0.5 text-sm transition-colors"
style={{
color: isActive ? "#fd4766" : "#6b7280",
background: isActive ? "rgba(253,71,102,0.08)" : "transparent",
fontWeight: isActive ? 600 : 400,
}}
>
<Icon size={17} />
<span className="flex-1">{item.label}</span>
{item.badge && (
<span
className="text-white text-xs px-1.5 py-0.5 rounded-full"
style={{ background: "#fd4766", fontSize: "0.65rem" }}
>
{item.badge}
</span>
)}
</a>
)
})}
</nav>
{/* User */}
<div className="border-t px-3 py-3">
<div className="flex items-center gap-2">
<div
className="w-8 h-8 rounded-full flex items-center justify-center text-white text-xs font-bold flex-shrink-0"
style={{ background: "#fd4766" }}
>
GS
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-semibold truncate">Gagan Singh</p>
<p className="text-xs text-gray-400">Founder</p>
</div>
<button className="text-gray-400 hover:text-gray-600">
<LogOut size={15} />
</button>
</div>
</div>
</aside>
{/* Main */}
<main className="flex-1 p-6">
<h1 className="text-xl font-bold">{active}</h1>
<p className="text-gray-500 text-sm mt-1">Main content area</p>
</main>
</div>
)
}2. Collapsible React Sidebar
Sidebar that collapses to icon-only mode with a toggle button and smooth transition.
import { useState } from "react"
import {
LayoutDashboard, Package, Users, BarChart3,
Settings, ChevronLeft, ChevronRight
} from "lucide-react"
const navItems = [
{ label: "Dashboard", icon: LayoutDashboard },
{ label: "Templates", icon: Package },
{ label: "Customers", icon: Users },
{ label: "Analytics", icon: BarChart3 },
{ label: "Settings", icon: Settings },
]
export default function CollapsibleSidebar() {
const [collapsed, setCollapsed] = useState(false)
const [active, setActive] = useState("Dashboard")
return (
<div className="flex h-screen bg-gray-50">
<aside
className="bg-white border-r flex flex-col transition-all duration-300 relative"
style={{ width: collapsed ? 64 : 240 }}
>
{/* Toggle button */}
<button
onClick={() => setCollapsed(!collapsed)}
className="absolute -right-3 top-6 w-6 h-6 bg-white border rounded-full flex items-center justify-center shadow-sm z-10 text-gray-400 hover:text-gray-600"
>
{collapsed ? <ChevronRight size={12} /> : <ChevronLeft size={12} />}
</button>
{/* Brand */}
<div className="px-4 py-4 border-b overflow-hidden whitespace-nowrap">
<div className="flex items-center gap-2">
<div
className="w-7 h-7 rounded flex items-center justify-center text-white text-xs font-bold flex-shrink-0"
style={{ background: "#fd4766" }}
>
AP
</div>
{!collapsed && (
<span className="font-bold" style={{ color: "#fd4766" }}>AdminPanel</span>
)}
</div>
</div>
{/* Nav */}
<nav className="flex-1 py-3 px-2">
{navItems.map((item) => {
const Icon = item.icon
const isActive = active === item.label
return (
<button
key={item.label}
onClick={() => setActive(item.label)}
className="flex items-center gap-3 px-3 py-2.5 rounded-md mb-0.5 w-full transition-colors overflow-hidden whitespace-nowrap"
title={collapsed ? item.label : undefined}
style={{
color: isActive ? "#fd4766" : "#6b7280",
background: isActive ? "rgba(253,71,102,0.08)" : "transparent",
fontWeight: isActive ? 600 : 400,
}}
>
<Icon size={17} className="flex-shrink-0" />
{!collapsed && (
<span className="text-sm">{item.label}</span>
)}
</button>
)
})}
</nav>
{/* User */}
<div className="border-t px-3 py-3 overflow-hidden whitespace-nowrap">
<div className="flex items-center gap-2">
<div
className="w-8 h-8 rounded-full flex items-center justify-center text-white text-xs font-bold flex-shrink-0"
style={{ background: "#fd4766" }}
>
GS
</div>
{!collapsed && (
<div className="min-w-0">
<p className="text-sm font-semibold truncate">Gagan Singh</p>
<p className="text-xs text-gray-400">Founder</p>
</div>
)}
</div>
</div>
</aside>
<main className="flex-1 p-6">
<p className="text-sm text-gray-500">Sidebar is {collapsed ? "collapsed" : "expanded"}</p>
<h1 className="text-xl font-bold mt-1">{active}</h1>
</main>
</div>
)
}3. Dark React Sidebar
Dark themed sidebar with grouped sections and notification badges.
import { useState } from "react"
import {
LayoutDashboard, Package, Users, BarChart3,
Settings, Bell, HelpCircle, LogOut
} from "lucide-react"
const sections = [
{
label: "Main",
items: [
{ label: "Dashboard", icon: LayoutDashboard },
{ label: "Templates", icon: Package, badge: "New" },
{ label: "Customers", icon: Users, count: 5 },
{ label: "Analytics", icon: BarChart3 },
],
},
{
label: "System",
items: [
{ label: "Notifications", icon: Bell, count: 3 },
{ label: "Settings", icon: Settings },
{ label: "Help", icon: HelpCircle },
],
},
]
export default function DarkSidebar() {
const [active, setActive] = useState("Dashboard")
return (
<div className="flex h-screen">
<aside
className="w-60 flex flex-col"
style={{ background: "#0d0d0d" }}
>
{/* Brand */}
<div className="px-4 py-4" style={{ borderBottom: "1px solid #1e1e2e" }}>
<div className="flex items-center gap-2">
<div
className="w-7 h-7 rounded flex items-center justify-center text-white text-xs font-bold"
style={{ background: "#fd4766" }}
>
AP
</div>
<span className="font-bold text-white">AdminPanel</span>
</div>
</div>
{/* Nav */}
<nav className="flex-1 py-3 px-2 overflow-y-auto">
{sections.map((section) => (
<div key={section.label} className="mb-4">
<p
className="text-xs font-semibold uppercase tracking-wider px-2 mb-2"
style={{ color: "#444" }}
>
{section.label}
</p>
{section.items.map((item) => {
const Icon = item.icon
const isActive = active === item.label
return (
<button
key={item.label}
onClick={() => setActive(item.label)}
className="flex items-center gap-3 px-3 py-2 rounded-md mb-0.5 w-full text-left text-sm transition-colors"
style={{
color: isActive ? "#fd4766" : "#888",
background: isActive ? "rgba(253,71,102,0.12)" : "transparent",
fontWeight: isActive ? 600 : 400,
}}
>
<Icon size={16} className="flex-shrink-0" />
<span className="flex-1">{item.label}</span>
{item.count && (
<span
className="text-white text-xs px-1.5 py-0.5 rounded-full"
style={{ background: "#fd4766", fontSize: "0.6rem" }}
>
{item.count}
</span>
)}
{item.badge && (
<span
className="text-xs px-1.5 py-0.5 rounded"
style={{ background: "rgba(253,71,102,0.2)", color: "#fd4766", fontSize: "0.62rem" }}
>
{item.badge}
</span>
)}
</button>
)
})}
</div>
))}
</nav>
{/* User */}
<div className="px-3 py-3" style={{ borderTop: "1px solid #1e1e2e" }}>
<div className="flex items-center gap-2">
<div
className="w-8 h-8 rounded-full flex items-center justify-center text-white text-xs font-bold flex-shrink-0"
style={{ background: "#fd4766" }}
>
GS
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-semibold text-white truncate">Gagan Singh</p>
<p className="text-xs" style={{ color: "#666" }}>Founder</p>
</div>
<button className="hover:text-white transition-colors" style={{ color: "#666" }}>
<LogOut size={15} />
</button>
</div>
</div>
</aside>
<main className="flex-1 p-6" style={{ background: "#f8f9fa" }}>
<h1 className="text-xl font-bold">{active}</h1>
</main>
</div>
)
}4. Mobile-Responsive Sidebar with shadcn/ui Sheet
Desktop sidebar + mobile drawer using shadcn/ui Sheet. Hamburger button triggers the mobile menu.
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
import {
LayoutDashboard, Package, Users,
BarChart3, Settings, Menu
} from "lucide-react"
const navItems = [
{ label: "Dashboard", icon: LayoutDashboard },
{ label: "Templates", icon: Package },
{ label: "Customers", icon: Users },
{ label: "Analytics", icon: BarChart3 },
{ label: "Settings", icon: Settings },
]
function SidebarContent({ active, setActive }: { active: string; setActive: (v: string) => void }) {
return (
<div className="flex flex-col h-full">
<div className="px-4 py-4 border-b">
<span className="font-bold text-lg" style={{ color: "#fd4766" }}>AdminPanel</span>
</div>
<nav className="flex-1 py-3 px-2">
{navItems.map((item) => {
const Icon = item.icon
const isActive = active === item.label
return (
<button
key={item.label}
onClick={() => setActive(item.label)}
className="flex items-center gap-3 px-3 py-2.5 rounded-md mb-0.5 w-full text-left text-sm transition-colors"
style={{
color: isActive ? "#fd4766" : "#6b7280",
background: isActive ? "rgba(253,71,102,0.08)" : "transparent",
fontWeight: isActive ? 600 : 400,
}}
>
<Icon size={17} />
{item.label}
</button>
)
})}
</nav>
<div className="border-t px-4 py-3">
<p className="text-xs text-gray-500">
Get templates at{" "}
<a href="https://lettstartdesign.com" className="font-semibold" style={{ color: "#fd4766" }}>
LettStartDesign
</a>
</p>
</div>
</div>
)
}
export default function MobileResponsiveSidebar() {
const [active, setActive] = useState("Dashboard")
return (
<div className="flex h-screen bg-gray-50">
{/* Desktop sidebar */}
<aside className="hidden lg:flex w-60 bg-white border-r">
<SidebarContent active={active} setActive={setActive} />
</aside>
{/* Main */}
<div className="flex-1 flex flex-col min-w-0">
{/* Mobile header */}
<header className="lg:hidden bg-white border-b px-4 py-3 flex items-center gap-3">
<Sheet>
<SheetTrigger asChild>
<Button variant="outline" size="icon" className="h-8 w-8">
<Menu size={16} />
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-60 p-0">
<SidebarContent active={active} setActive={setActive} />
</SheetContent>
</Sheet>
<span className="font-bold" style={{ color: "#fd4766" }}>AdminPanel</span>
</header>
<main className="flex-1 p-6">
<h1 className="text-xl font-bold">{active}</h1>
<p className="text-gray-500 text-sm mt-1">
Resize to mobile to see the hamburger menu
</p>
</main>
</div>
</div>
)
}Frequently Asked Questions
Need a Full React + Next.js Dashboard Template?
Get a complete React + Next.js dashboard with 50+ components — built by the same team behind BootstrapPlanet.
Browse Templates →Use code FIRST30 for 30% off.