App Shell / Layout
Preview: Inset
Page frame: sidebar + header + scrolling main region with the right landmarks.
Import
import { AppShell } from '@marbleui/ui'Props
<AppShell>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactNode | — | |
| className | string | — | |
| header | ReactNode | — | |
| sidebar | ReactNode | — | |
| variant | 'flush' | 'inset' | 'flush' | "inset": content sits in a rounded panel next to the sidebar. "flush": edge to edge. |
Usage notes
Page frame with an optional sidebar, a header and a scrolling main region. For just a navigation column use Sidebar; for a centered marketing page you don’t need it.
<AppShell sidebar={<Sidebar>{nav}</Sidebar>} header={<Breadcrumbs>{crumbs}</Breadcrumbs>} variant="inset">
<PageContent />
</AppShell>
- It fills its parent (
h-full), so the parent needs a height, e.g.h-screen. - Renders
headerin a<header>andchildrenin<main id="main">, which scrolls on its own. Useid="main"as the target of a skip link, and use only oneAppShellper page. variantisflush(default) orinset, where the content sits in a rounded panel beside the sidebar.
Example source · show· hide
import { Breadcrumb, Breadcrumbs } from '../breadcrumbs'
import { Button } from '../button'
import { SearchField } from '../search-field'
import { Sidebar, SidebarItem, SidebarSection } from '../sidebar'
import { Stat } from '../stat'
import { AppShell } from './app-shell'
const Dot = () => <span className="size-1.5 rounded-full bg-current" />
const Nav = () => (
<Sidebar collapsible header={<span className="font-semibold">Acme</span>} variant="plain" className="border-r-0 bg-transparent">
<SidebarSection>
<SidebarItem href="#" icon={<Dot />} isCurrent>Overview</SidebarItem>
<SidebarItem href="#" icon={<Dot />}>Customers</SidebarItem>
<SidebarItem href="#" icon={<Dot />}>Invoices</SidebarItem>
</SidebarSection>
</Sidebar>
)
const Page = () => (
<div className="grid grid-cols-3 gap-3 p-5">
<Stat label="MRR" value="$48.2k" delta={6.1} />
<Stat label="Customers" value="1,204" delta={2.3} />
<Stat label="Churn" value="1.9%" delta={-0.4} invert />
</div>
)
const Frame = ({ variant }: { variant: 'flush' | 'inset' }) => (
<div className="h-[26rem] w-[50rem] overflow-hidden rounded-xl border border-border">
<AppShell
variant={variant}
sidebar={<Nav />}
header={
<>
<Breadcrumbs><Breadcrumb href="#">Acme</Breadcrumb><Breadcrumb>Overview</Breadcrumb></Breadcrumbs>
<SearchField aria-label="Search" className="ml-auto w-56" variant="filled" shortcut="⌘K" />
<Button size="sm">New invoice</Button>
</>
}
>
<Page />
</AppShell>
</div>
)
export const styles = { Inset: () => <Frame variant="inset" />, Flush: () => <Frame variant="flush" /> }Source: packages/ui/src/app-shell/app-shell.tsx
Catalog
- Category
- layout
- Priority
- Core
- Primitive
- composed