# Application shell

Navigate a responsive workspace with a sidebar, header, and content region.

Application shell

[Complete TSX file](/examples/application-shell/default.tsx)

```tsx
"use client";

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/siteplane/components/card";
import {
  DashboardShell,
  DashboardShellHeader,
  DashboardShellInset,
  DashboardShellMain,
  DashboardShellPrimary,
  DashboardShellSidebar,
  DashboardShellSidebarHeader,
  DashboardShellSidebarTrigger,
} from "@/siteplane/components/dashboard-shell";
import {
  SettingsList,
  SettingsListItem,
} from "@/siteplane/components/settings-list";
import {
  SidebarBrand,
  SidebarContent,
  SidebarGroup,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
} from "@/siteplane/components/sidebar";
import { Switch } from "@/siteplane/components/switch";
import { FolderIcon, HomeIcon, SettingsIcon } from "lucide-react";
import { useState } from "react";

const navigation = [
  { label: "Overview", icon: HomeIcon },
  { label: "Projects", icon: FolderIcon },
  { label: "Settings", icon: SettingsIcon },
] as const;

type Page = (typeof navigation)[number]["label"];

const pages = {
  Overview: {
    title: "Recent activity",
    description: "Updates from your projects this week.",
    items: [
      "Website redesign moved to review",
      "Ben Carter joined Mobile application",
      "Sofia Nguyen shared Customer research",
    ],
  },
  Projects: {
    title: "Active projects",
    description: "Projects your team is working on.",
    items: ["Website redesign", "Mobile application", "Customer research"],
  },
};

export default function Example() {
  const [active, setActive] = useState<Page>("Overview");
  const [notifications, setNotifications] = useState(true);
  return (
    <div className="relative h-[32rem] w-full overflow-hidden [transform:translateZ(0)]">
      <DashboardShell className="h-full min-h-0">
        <DashboardShellSidebar className="h-full" collapsible="offcanvas">
          <DashboardShellSidebarHeader>
            <SidebarBrand>Acme</SidebarBrand>
          </DashboardShellSidebarHeader>
          <SidebarContent>
            <SidebarGroup>
              <SidebarMenu>
                {navigation.map(({ label, icon: Icon }) => (
                  <SidebarMenuItem key={label}>
                    <SidebarMenuButton
                      active={active === label}
                      onClick={() => setActive(label)}
                    >
                      <Icon />
                      <span>{label}</span>
                    </SidebarMenuButton>
                  </SidebarMenuItem>
                ))}
              </SidebarMenu>
            </SidebarGroup>
          </SidebarContent>
        </DashboardShellSidebar>
        <DashboardShellInset>
          <DashboardShellHeader>
            <DashboardShellSidebarTrigger />
            <h1 className="font-semibold">{active}</h1>
          </DashboardShellHeader>
          <DashboardShellPrimary>
            <DashboardShellMain>
              {active === "Settings" ? (
                <SettingsList
                  title="Notifications"
                  description="Choose which updates you receive."
                >
                  <SettingsListItem
                    title="Project updates"
                    description="Notify me when a project changes."
                    control={(props) => (
                      <Switch
                        {...props}
                        checked={notifications}
                        onCheckedChange={setNotifications}
                      />
                    )}
                  />
                </SettingsList>
              ) : (
                <Card>
                  <CardHeader>
                    <CardTitle>{pages[active].title}</CardTitle>
                    <CardDescription>
                      {pages[active].description}
                    </CardDescription>
                  </CardHeader>
                  <CardContent>
                    <ul className="grid gap-3 text-sm">
                      {pages[active].items.map((item) => (
                        <li key={item}>{item}</li>
                      ))}
                    </ul>
                  </CardContent>
                </Card>
              )}
            </DashboardShellMain>
          </DashboardShellPrimary>
        </DashboardShellInset>
      </DashboardShell>
    </div>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/), then install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/application-shell
```

## Files

The command installs `src/components/application-shell.tsx` and its component dependencies. Render its default export in your application. The [registry item](/registry/application-shell.json) and the Code panel contain the same source.

## Try the workflow

Choose Overview, Projects, or Settings to change the page in the header and the content area; Settings contains a notification switch. The header button hides and shows the sidebar on desktop and opens it as a sheet at narrow widths.

## Composition and adaptation

DashboardShell owns the application layout and SidebarProvider; Sidebar parts render the navigation, and Card and SettingsList provide the page content.

| Area                    | Details                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Data                    | `active` selects the page; `pages` holds its content; `notifications` holds the switch value.                                                                                                                                                                                                                                                                                                                                                                          |
| Callbacks               | Navigation clicks update `active`; Switch `onCheckedChange` updates `notifications`.                                                                                                                                                                                                                                                                                                                                                                                   |
| Included states         | Active navigation (`aria-current`), expanded and hidden desktop sidebar, mobile sheet and a settings page.                                                                                                                                                                                                                                                                                                                                                             |
| Connect to your product | Replace `active` with your router and the page content with your routes. For a viewport-sized app, remove the bounded wrapper and the `h-full` and `min-h-0` classes on `DashboardShell` and its sidebar; the top bar then stays in view while the content scrolls, and `stickyHeader={false}` lets the whole page scroll instead. Read the cookie and keyboard shortcut notes on [Sidebar](/docs/components/sidebar/). No authentication or backend data is included. |
