AppShell is the page shell for an application. It provides slots for top navigation, side navigation, banners, and main content. Use it as the root wrapper for every page. It handles responsive mobile navigation and skip-to-content automatically. Configure side nav collapse on SideNav with its collapsible prop.
Preview loads on approachPreview loads on approach
Exact source · app-shell-showcase
// Copyright (c) Meta Platforms, Inc. and affiliates.
'use client';
import {AppShell} from '@astryxdesign/core/AppShell';
import {VStack} from '@astryxdesign/core/Stack';
import {Heading, Text} from '@astryxdesign/core/Text';
import {NavIcon} from '@astryxdesign/core/NavIcon';
import {
SideNav,
SideNavHeading,
SideNavItem,
SideNavSection,
} from '@astryxdesign/core/SideNav';
import {
ChartBarIcon,
DocumentTextIcon,
UsersIcon,
} from '@heroicons/react/24/outline';
import {HomeIcon} from '@heroicons/react/24/solid';
import {CubeIcon} from '@heroicons/react/24/outline';
export default function AppShellShowcase() {
return (
<AppShell
contentPadding={6}
style={{height: '100%', minHeight: 0, width: '100%'}}
sideNav={
<SideNav
header={
<SideNavHeading
icon={
<NavIcon icon={<CubeIcon style={{width: 16, height: 16}} />} />
}
heading="App Shell"
headingHref="#"
/>
}>
<SideNavSection title="Main" isHeaderHidden>
<SideNavItem label="Home" icon={HomeIcon} isSelected href="#" />
<SideNavItem label="Reports" icon={ChartBarIcon} href="#" />
<SideNavItem label="Documents" icon={DocumentTextIcon} href="#" />
<SideNavItem label="Team" icon={UsersIcon} href="#" />
</SideNavSection>
</SideNav>
}>
<VStack gap={4}>
<Heading level={3}>Page Content</Heading>
<Text type="body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris.
</Text>
</VStack>
</AppShell>
);
}
Usage
AppShell is the page shell for an application. It provides slots for top navigation, side navigation, banners, and main content. Use it as the root wrapper for every page. It handles responsive mobile navigation and skip-to-content automatically. Configure side nav collapse on SideNav with its collapsible prop.
Choose the right height: use "fill" for dashboards with internal scrolling and "auto" for pages that grow with content.
Set contentPadding based on content type: 4 for forms and settings, 0 for tables and dashboards.
Give every nav slot an accessible name. AppShell renders TopNav and SideNav as separate navigation landmarks, and a screen reader lists them by name, so pass label to each one.
Start the page heading inside children. AppShell owns the skip link, the banner landmark and the main landmark, but it renders no heading, so the first heading in the content area is the page h1.
Nest one AppShell inside another; it's the outermost layout frame.
Use for sub-page layouts; use Layout for content areas within AppShell.
Add your own skip link or <main> element. AppShell already renders both, and a second main landmark makes the first ambiguous.
Typed props
Prop
Type and behavior
children
ReactNode
Main content area, rendered inside a <main> element.
Padding for the main content area. Set based on the dominant content pattern: 4 (16px) for forms/settings/text, 0 for dashboards/maps/tables. Override individual sections with Section.
Mobile navigation configuration. Accepts false (disable), a config object (tune auto behavior), or ReactNode (full custom drawer). The config object is {hasToggle?: boolean, isOpen?: boolean, onOpenChange?: (isOpen: boolean) => void, content?: ReactNode, breakpoint?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'none', defaultIsMobile?: boolean}; breakpoint defaults to 'md', resolves through the nearest Theme's widthBreakpoints, and switches to the wider layout at equality. 'none' is always non-mobile and ignores defaultIsMobile.
Height behavior: 'fill' makes the shell fill the viewport (100dvh) with independent scroll containers; 'auto' lets the shell grow with content and uses sticky positioning for nav.
Navigation background style controlling how nav areas contrast with content. 'wash' uses wash background, 'surface' uses surface background, 'section' adds dividers between nav and content, 'elevated' uses wash nav with elevated surface content and border radius.
xstyle
StyleXStyles
StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value, not an inline style object like style={{}}.
Anatomy
Page shell · required
Outermost application frame that owns page-level navigation, responsive shell behavior, and the main content landmark.
Skip link · required
First focusable element on the page. Visually hidden until focused, then moves focus to the main content area.
Banner · optional
The banner slot, for system-wide announcements. Renders above the top nav, inside the banner landmark.
Top navigation · optional
The topNav slot, typically TopNav. Below the mobile breakpoint it becomes a compact bar carrying the nav toggle.
Side navigation · optional
The sideNav slot, typically SideNav. Inline above the breakpoint, moved into the mobile drawer below it.
Main content · required
children, rendered in the main landmark. Scrolls internally when height is fill, and with the page when it is auto.
Mobile nav drawer · optional
Generated below the breakpoint from the nav slots unless mobileNav disables or replaces it. A modal dialog: it traps focus and returns focus to the toggle on close.