AppShell

Frames the application with header, sidebar, main and optional aside regions, sharing sidebar state.

import { AppShell } from "@usefragments/ui";Source

Preview

Loading Default Layout preview…

import { ChartBar as OverviewIcon, GitPullRequest as PullRequestIcon, ListChecks as FindingsIcon, Palette as TokensIcon } from "@phosphor-icons/react";
import { AppShell } from '@usefragments/ui';
import { Avatar } from '@usefragments/ui';
import { Badge } from '@usefragments/ui';
import { Header } from '@usefragments/ui';
import { Input } from '@usefragments/ui';

Usage

Use when

  • Dashboards and internal tools
  • Apps with persistent sidebar navigation
  • Layouts needing header, sidebar and main together
  • Navigation that must become a modal panel on small screens

Avoid when

  • Marketing and landing pages
  • Content-first sites with no app navigation
  • Single-screen tools with no chrome

Implementation notes

  • Put a Main inside AppShell.Main: Main owns the 16 gutter and the <main> landmark; the slot adds neither
  • AppShell is the Sidebar.Provider: set sidebar state here, not on AppShell.Sidebar
  • Columns follow the slots' own token widths, so collapsing is instant and an absent slot takes no room
  • Every region is canvas, the aside included; hairlines separate them
  • Header.Trigger shows on small screens and when the rail is off the canvas
  • The aside drops under main below lg

Installation

pnpm add @usefragments/ui

Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Anatomy

Import AppShell once, then compose its parts with dot notation.

  • AppShell.Header
  • AppShell.Sidebar
  • AppShell.Main
  • AppShell.Aside

Header across the top, the rail below it, the page beside it.

import { ChartBar as OverviewIcon, GitPullRequest as PullRequestIcon, ListChecks as FindingsIcon, Palette as TokensIcon } from "@phosphor-icons/react";
import { AppShell } from '@usefragments/ui';
import { Avatar } from '@usefragments/ui';
import { Badge } from '@usefragments/ui';
import { Header } from '@usefragments/ui';
import { Input } from '@usefragments/ui';
import { Main } from '@usefragments/ui';
import { Sidebar } from '@usefragments/ui';
import { Table } from '@usefragments/ui';

const checks = [
  { repo: 'web', result: <Badge tone="success">Passing</Badge> },
  { repo: 'checkout', result: <Badge tone="warning">3 findings</Badge> },
  { repo: 'marketing', result: <Badge tone="success">Passing</Badge> },
];

<AppShell>
  <AppShell.Header>

Examples

Dashboard

Full-height rail with the brand and grouped sections, a header with search and actions, a page of metrics.

Loading Dashboard preview…

import { ChartBar as OverviewIcon, Gear as SettingsIcon, GitPullRequest as PullRequestIcon, ListChecks as FindingsIcon, Palette as TokensIcon } from "@phosphor-icons/react";
import { AppShell } from '@usefragments/ui';
import { Avatar } from '@usefragments/ui';
import { Button } from '@usefragments/ui';
import { Card } from '@usefragments/ui';
import { Grid } from '@usefragments/ui';

With Aside Panel

A details panel beside the page, at the wide sidebar width: the list stays in view while one item is open.

Loading With Aside Panel preview…

import { AppShell } from '@usefragments/ui';
import { Badge } from '@usefragments/ui';
import { Button } from '@usefragments/ui';
import { Header } from '@usefragments/ui';
import { Main } from '@usefragments/ui';
import { Stack } from '@usefragments/ui';

Offcanvas Rail

Collapsing takes the rail off the canvas for reading; Header.Trigger brings it back.

Loading Offcanvas Rail preview…

import { BookOpen as GuideIcon, FileText as ReferenceIcon } from "@phosphor-icons/react";
import { AppShell } from '@usefragments/ui';
import { Header } from '@usefragments/ui';
import { Main } from '@usefragments/ui';
import { Sidebar } from '@usefragments/ui';
import { Text } from '@usefragments/ui';

API reference

Component props
Prop
Type
Default
Description
Aside.childrenRequiredReact.ReactNodeNot set—
Aside.visibleboolean | undefinedNot setRender the panel.
childrenRequiredReact.ReactNodeNot setAppShell.Header, AppShell.Sidebar, AppShell.Main, AppShell.Aside
collapsedboolean | undefinedNot setDesktop: the rail is collapsed (controlled)
collapsibleSidebarCollapsible | undefinednoneiconoffcanvas"icon"How the rail collapses: to its glyphs, off the canvas, or never
defaultCollapsedboolean | undefinedfalseDesktop: the rail starts collapsed
defaultOpenboolean | undefinedfalseMobile: the panel starts open
enableKeyboardShortcutboolean | undefinedtrueToggle the sidebar with Cmd/Ctrl+B
Header.childrenRequiredReact.ReactNodeNot set—
layoutAppShellLayout | undefineddefaultsidebar"default"`default`: the header spans the top. `sidebar`: the rail runs the full height and the header sits beside it.
Main.childrenRequiredReact.ReactNodeNot set—
onCollapsedChange((collapsed: boolean) => void) | undefinedNot setCalled when the rail collapses or expands
onOpenChange((open: boolean) => void) | undefinedNot setCalled when the mobile panel opens or closes
openboolean | undefinedNot setMobile: the panel is open (controlled)
Sidebar.aria-labelstring | undefinedNot setAccessible label for the sidebar.
Sidebar.childrenRequiredReact.ReactNodeNot set—

Accessibility

  • AppShell.Header is the banner landmark; a Header inside it renders a div
  • Main renders <main id="main-content"> for Header.SkipLink
  • Below md the sidebar is a modal dialog with a focus trap, Escape and a scrim
  • Cmd/Ctrl+B toggles the sidebar
  • ThemeparentAppShell sits inside the Theme provider
  • HeaderchildHeader goes inside AppShell.Header
  • SidebarchildSidebar content goes inside AppShell.Sidebar; AppShell is its provider
  • MainchildMain goes inside AppShell.Main and owns the gutter and the <main> landmark

Next steps