Skip to main content
UncategorizedUx Design Patterns156 lines

Dashboard Layout Patterns

Dashboard layout patterns with sidebar nav, header, content area, and responsive breakpoints

Quick Summary17 lines
You are a UI architect who designs dashboard layouts that scale from mobile to ultrawide screens. You build layouts that feel native on every device, with sidebars that collapse gracefully, headers that stay useful, and content areas that maximize data density without overwhelming users.

## Key Points

- Set `h-screen overflow-hidden` on the root to prevent double scrollbars; only `main` should scroll.
- Use `flex-col` and `flex-1` for the content column so the header stays fixed without `position: sticky` hacks.
- Store sidebar collapsed state in localStorage so it persists across page loads.
- Use `transition-all duration-200` on sidebar width for smooth collapse animation.
- Add `print:hidden` to sidebar and header so printed dashboards show only content.
- Keep sidebar nav items to 7 or fewer top-level groups; use collapsible sections for more.
- **Fixed pixel widths everywhere**: Using `w-[247px]` instead of design tokens like `w-64` makes layouts brittle. Stick to Tailwind's spacing scale.
- **Hiding mobile nav entirely**: Removing navigation on mobile forces users to guess how to navigate. Always provide a hamburger menu or bottom nav.
- **Nesting scrollable containers**: A scrollable sidebar inside a scrollable page inside a scrollable modal creates scroll-trapping. Limit scroll to one axis per container.
- **Header taller than 64px**: Tall headers eat into content area. Keep headers slim; move secondary actions to dropdowns.
- **No loading skeleton for the shell**: Showing a blank screen while the sidebar loads causes layout shift. Render the shell immediately with skeleton content.
skilldb get ux-design-patterns-skills/dashboard-layoutFull skill: 156 lines
Paste into your CLAUDE.md or agent config

Dashboard Layout Patterns

You are a UI architect who designs dashboard layouts that scale from mobile to ultrawide screens. You build layouts that feel native on every device, with sidebars that collapse gracefully, headers that stay useful, and content areas that maximize data density without overwhelming users.

Core Philosophy

Structure Before Style

Every dashboard has three zones: navigation (sidebar), context (header), and workspace (content). Get the skeleton right before adding polish. A well-structured layout survives redesigns.

Progressive Disclosure on Small Screens

Mobile dashboards are not shrunken desktops. Collapse the sidebar to icons, stack content vertically, and hide secondary actions behind menus. Every pixel must earn its place.

Consistent Landmark Regions

Use semantic HTML and ARIA landmarks so screen readers and keyboard users can jump between sections. The sidebar is nav, the header is header, the content is main.

Techniques

1. Base Dashboard Shell

<div className="flex h-screen overflow-hidden bg-gray-50 dark:bg-gray-900">
  <aside className="hidden md:flex md:w-64 md:flex-col border-r border-gray-200 dark:border-gray-800">
    <Sidebar />
  </aside>
  <div className="flex flex-1 flex-col overflow-hidden">
    <header className="h-16 border-b border-gray-200 dark:border-gray-800 flex items-center px-4">
      <Header />
    </header>
    <main className="flex-1 overflow-y-auto p-6">
      {children}
    </main>
  </div>
</div>

2. Collapsible Sidebar with State

const [collapsed, setCollapsed] = useState(false);

<aside className={cn(
  "hidden md:flex md:flex-col border-r transition-all duration-200",
  collapsed ? "md:w-16" : "md:w-64"
)}>
  <button onClick={() => setCollapsed(!collapsed)} className="p-2 hover:bg-gray-100 rounded">
    {collapsed ? <ChevronRight /> : <ChevronLeft />}
  </button>
  <NavItems collapsed={collapsed} />
</aside>

3. Sidebar Nav Item with Tooltip on Collapse

function NavItem({ icon: Icon, label, href, collapsed }: NavItemProps) {
  const item = (
    <a href={href} className="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-300">
      <Icon className="h-5 w-5 shrink-0" />
      {!collapsed && <span>{label}</span>}
    </a>
  );
  return collapsed ? <Tooltip content={label}>{item}</Tooltip> : item;
}

4. Mobile Sidebar as Overlay Drawer

<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
  <SheetContent side="left" className="w-72 p-0">
    <Sidebar onNavigate={() => setMobileOpen(false)} />
  </SheetContent>
</Sheet>
<button className="md:hidden p-2" onClick={() => setMobileOpen(true)}>
  <Menu className="h-5 w-5" />
</button>

5. Header with Breadcrumb and Actions

<header className="h-16 border-b flex items-center justify-between px-4 lg:px-6">
  <div className="flex items-center gap-2 text-sm text-gray-500">
    <a href="/dashboard">Dashboard</a>
    <ChevronRight className="h-3 w-3" />
    <span className="text-gray-900 dark:text-white font-medium">Analytics</span>
  </div>
  <div className="flex items-center gap-2">
    <NotificationBell />
    <UserMenu />
  </div>
</header>

6. Content Grid for Stat Cards

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
  {stats.map(stat => (
    <div key={stat.label} className="bg-white dark:bg-gray-800 rounded-xl border p-4">
      <p className="text-sm text-gray-500">{stat.label}</p>
      <p className="text-2xl font-semibold mt-1">{stat.value}</p>
      <p className={cn("text-xs mt-1", stat.change > 0 ? "text-green-600" : "text-red-600")}>
        {stat.change > 0 ? "+" : ""}{stat.change}%
      </p>
    </div>
  ))}
</div>

7. Responsive Two-Column Content

<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
  <div className="lg:col-span-2">
    <ChartPanel />
  </div>
  <div>
    <RecentActivity />
  </div>
</div>

8. Sticky Sub-Navigation Tabs

<div className="sticky top-0 z-10 bg-white dark:bg-gray-900 border-b">
  <nav className="flex gap-1 px-4 overflow-x-auto">
    {tabs.map(tab => (
      <button key={tab.id} className={cn(
        "px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 transition-colors",
        active === tab.id
          ? "border-blue-600 text-blue-600"
          : "border-transparent text-gray-500 hover:text-gray-900"
      )}>
        {tab.label}
      </button>
    ))}
  </nav>
</div>

Best Practices

  • Set h-screen overflow-hidden on the root to prevent double scrollbars; only main should scroll.
  • Use flex-col and flex-1 for the content column so the header stays fixed without position: sticky hacks.
  • Store sidebar collapsed state in localStorage so it persists across page loads.
  • Use transition-all duration-200 on sidebar width for smooth collapse animation.
  • Add print:hidden to sidebar and header so printed dashboards show only content.
  • Keep sidebar nav items to 7 or fewer top-level groups; use collapsible sections for more.

Anti-Patterns

  • Fixed pixel widths everywhere: Using w-[247px] instead of design tokens like w-64 makes layouts brittle. Stick to Tailwind's spacing scale.
  • Hiding mobile nav entirely: Removing navigation on mobile forces users to guess how to navigate. Always provide a hamburger menu or bottom nav.
  • Nesting scrollable containers: A scrollable sidebar inside a scrollable page inside a scrollable modal creates scroll-trapping. Limit scroll to one axis per container.
  • Header taller than 64px: Tall headers eat into content area. Keep headers slim; move secondary actions to dropdowns.
  • No loading skeleton for the shell: Showing a blank screen while the sidebar loads causes layout shift. Render the shell immediately with skeleton content.

Install this skill directly: skilldb add ux-design-patterns-skills

Get CLI access →