Skip to content

AppShell

AppShell

<AppShell
header={<TopBar onMenuClick={() => setOpen(true)} />}
sidebar={
<Sidebar>
<Sidebar.Group>
<Sidebar.Item href="/" active>Dashboard</Sidebar.Item>
<Sidebar.Item href="/users">Users</Sidebar.Item>
</Sidebar.Group>
</Sidebar>
}
>
<PageHeader title="Dashboard" />
{/* page content */}
</AppShell>

Mobile drawer

const [open, setOpen] = useState(false);
<>
<AppShell header={<TopBar onMenu={() => setOpen(true)} />} sidebar={<Sidebar></Sidebar>}>
{content}
</AppShell>
<Sheet side="start" isOpen={open} onOpenChange={setOpen}>
<Sidebar></Sidebar>
</Sheet>
</>

Install: @helixui/core

import { AppShell } from '@helixui/core'

status: stable · since: 0.2.0

Tags: layout, shell, header, sidebar, responsive, top-level

Anatomy

┌─ header (sticky top) ────────────────────────────────┐
│ logo … search … avatar │
├──────────┬───────────────────────────────────────────┤
│ sidebar │ <main> │
│ (≥768px) │ PageHeader │
│ │ <page content> │
│ │ │
│ │ │
└──────────┴───────────────────────────────────────────┘
↑ sidebar collapses below collapseBreakpoint;
↑ caller must render hamburger + Sheet for mobile.

Layout

  • displaygrid
  • widthfill
  • heightfill
  • intrinsicSizefills viewport, sidebar 240px (configurable), header sticky top
  • stackablefalse
  • fullBleedtrue

Visual

A desktop-first chrome: a sticky top header (border-bottom by border.default), a fixed-width sidebar on the left, and a scrolling main column. Below collapseBreakpoint the sidebar disappears and main becomes full-bleed; the caller is responsible for wiring a hamburger + Sheet drawer to expose it again.

Props

NameTypeDefaultDescription
sidebarReactNodeLeft rail content. Shown on desktop, hidden below the breakpoint.
headerReactNodeSticky top bar.
sidebarWidthnumber | string240Sidebar width. Number → px.
collapseBreakpointnumber768Min viewport (px) for sidebar to be visible.

Slots

  • children — main content area

Tokens used

color.bg.surface.default, color.text.primary, color.border.default

Accessibility

Notes

  • Sidebar uses <aside>; main uses <main>. Wrap nav inside the sidebar in <nav aria-label> (helixui Sidebar component does this).
  • On mobile the sidebar is display:none. To expose it, render a hamburger in header that opens a Sheet side="start" with the same sidebar content.

Composes with

ComponentRelationNote
SidebarslotFills the sidebar prop.
PageHeaderchildCommon first child of children to title each route.
SheetsiblingPair with Sheet (side=‘start’) for the mobile sidebar drawer.
SafeAreaparentWrap AppShell in SafeArea on mobile builds.

Prompt examples

These are the AI prompt → JSX mappings used by the helixui prompt DSL and integrations like Cursor / Claude Code.

scaffold a logged-in app layout

“give me an app layout with a top bar and side nav”

<AppShell
header={<TopBar />}
sidebar={<Sidebar><Sidebar.Group><Sidebar.Item href="/" active>Home</Sidebar.Item></Sidebar.Group></Sidebar>}
>
<PageHeader title="Home" />
{/* page */}
</AppShell>

responsive — show the sidebar in a drawer on mobile

“make the sidebar slide in on mobile”

const [open, setOpen] = useState(false);
<>
<AppShell
header={<TopBar onMenu={() => setOpen(true)} />}
sidebar={<Sidebar></Sidebar>}
>
{content}
</AppShell>
<Sheet side="start" isOpen={open} onOpenChange={setOpen}>
<Sidebar></Sidebar>
</Sheet>
</>

narrow icon-only sidebar

“use a 64px icon-only sidebar”

<AppShell sidebarWidth={64} sidebar={<IconRail />}>
...
</AppShell>