Skip to content

App Shell Layout

App shellsMIT
View in Storybook

Full composable app shell with sidebar, navbar, page header, and body slots.

bun add @otfdashkit/ui

Preview

App Shell Layout

Code

import React, { useState } from 'react'
import { Home, Settings, Users, FileText, BarChart3, Bell, Search, Plus, LogOut } from 'lucide-react'
import { AppShell, AppShellSidebar, AppShellMain } from '@otfdashkit/ui/layouts-app-shell'
import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarItem, SidebarSeparator } from '@otfdashkit/ui/layouts-sidebar'
import { Navbar, NavbarBrand, NavbarContent, NavbarItem } from '@otfdashkit/ui/layouts-navbar'
import { Page, PageHeader, PageTitle, PageDescription, PageActions, PageBody } from '@otfdashkit/ui/layouts-page'

const SidebarDemo = ({ collapsed, onCollapse }: { collapsed: boolean; onCollapse: () => void }) => (
  <Sidebar collapsed={collapsed} onCollapse={onCollapse} width="220px">
    <SidebarHeader>
      {!collapsed && <span className="font-semibold text-sm text-[hsl(var(--foreground))]">MyApp</span>}
    </SidebarHeader>
    <SidebarContent>
      <SidebarGroup>
        <SidebarGroupLabel>Main</SidebarGroupLabel>
        <SidebarItem icon={<Home size={16} />} label="Dashboard" active />
        <SidebarItem icon={<BarChart3 size={16} />} label="Analytics" badge={3} />
        <SidebarItem icon={<Users size={16} />} label="Team" />
        <SidebarItem icon={<FileText size={16} />} label="Documents" />
      </SidebarGroup>
      <SidebarSeparator />
      <SidebarGroup>
        <SidebarGroupLabel>Account</SidebarGroupLabel>
        <SidebarItem icon={<Bell size={16} />} label="Notifications" badge="9+" />
        <SidebarItem icon={<Settings size={16} />} label="Settings" />
      </SidebarGroup>
    </SidebarContent>
    <SidebarFooter>
      <SidebarItem icon={<LogOut size={16} />} label="Sign out" />
    </SidebarFooter>
  </Sidebar>
)

export function Example() {
  const [collapsed, setCollapsed] = useState(false)
  return (
    <div className="min-h-[500px] border border-[hsl(var(--border))] rounded-lg overflow-hidden">
      <AppShell style={{ height: 500 }}>
        <AppShellSidebar>
          <SidebarDemo collapsed={collapsed} onCollapse={() => setCollapsed(v => !v)} />
        </AppShellSidebar>
        <AppShellMain>
          <Navbar border sticky={false}>
            <NavbarBrand><span className="font-semibold text-sm">Dashboard</span></NavbarBrand>
            <NavbarContent align="end">
              <NavbarItem><Search size={16} className="text-[hsl(var(--muted-foreground))]" /></NavbarItem>
              <NavbarItem><Bell size={16} className="text-[hsl(var(--muted-foreground))]" /></NavbarItem>
            </NavbarContent>
          </Navbar>
          <Page>
            <PageHeader>
              <div><PageTitle>Dashboard</PageTitle><PageDescription>Welcome back! Here's what's happening.</PageDescription></div>
              <PageActions>
                <button className="flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-md bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]">
                  <Plus size={14} /> New Project
                </button>
              </PageActions>
            </PageHeader>
            <PageBody>
              <div className="grid gap-4" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%, 200px), 1fr))' }}>
                {['Projects', 'Team Members', 'Open Issues', 'Deployments'].map((label, i) => (
                  <div key={label} className="p-4 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))]">
                    <div className="text-2xl font-bold text-[hsl(var(--foreground))]">{(i + 1) * 12}</div>
                    <div className="text-sm text-[hsl(var(--muted-foreground))]">{label}</div>
                  </div>
                ))}
              </div>
            </PageBody>
          </Page>
        </AppShellMain>
      </AppShell>
    </div>
  )
}

Free with the SDK. This block ships in @otfdashkit/ui under the MIT license.

Props

Props declared in the AppShell source. Inherited HTML attributes are not listed.

PropTypeDefaultDescription
defaultCollapsedbooleanfalse