XDSAppShell@xds/core · AppShell
Usage
The outermost layout for an application. Provides slots for top navigation, side navigation, banners, and main content. Use it as the root wrapper for every page — it handles responsive collapse, skip-to-content, and mobile navigation automatically.Best practices
| Guidance | Practices |
|---|---|
| Do | Choose the right height — use "fill" for dashboards with internal scrolling and "auto" for pages that grow with content. |
| Do | Set `contentPadding` based on content type — 4 for forms and settings, 0 for tables and dashboards. |
| Don't | Nest one AppShell inside another — it's the outermost layout frame. |
| Don't | Use for sub-page layouts — use Layout for content areas within AppShell. |
Import
tsimport {XDSAppShell} from '@xds/core/AppShell'
Props
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Main content area, rendered inside a <main> element. |
contentPadding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 (default: 0) | 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 XDSSection. |
topNav | ReactNode | Top navigation slot, typically XDSTopNav. |
sideNav | ReactNode | Side navigation slot, typically XDSSideNav. |
mobileNav | ReactNode | Mobile navigation configuration. Accepts false (disable), config object (tune auto behavior), or ReactNode (full custom drawer). |
banner | ReactNode | Banner slot for system-wide announcements, placed above the topNav. |
height | 'fill' | 'auto' (default: 'fill') | 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. |
isSideNavCollapsed | boolean | Whether the sideNav is collapsed (controlled mode). |
defaultIsSideNavCollapsed | boolean (default: false) | Initial collapsed state for uncontrolled mode. |
onSideNavCollapsedChange | (isCollapsed: boolean) => void | Callback fired when the sideNav collapse state changes. |
variant | 'wash' | 'surface' | 'section' | 'elevated' (default: 'elevated') | 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={{}}. |
Examples
Common configurations, variations, and states.AppShell — Content OnlyMinimal shell with no navigation, useful for full-bleed pages, auth screens, or embedded views.
tsx'use client';import {XDSAppShell} from '@xds/core/AppShell';import {XDSVStack} from '@xds/core/Stack';import {XDSHeading, XDSText} from '@xds/core/Text';import stylex from '@stylexjs/stylex';const styles = stylex.create({fit: {height: '100%',minHeight: 0,},});export default function AppShellContentOnly() {return (<XDSAppShell contentPadding={6} xstyle={styles.fit}><XDSVStack gap={4}><XDSHeading level={3}>Page Content</XDSHeading><XDSText type="body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim adminim veniam, quis nostrud exercitation ullamco laboris.</XDSText></XDSVStack></XDSAppShell>);}
AppShell — Side Nav OnlyApp shell with SideNav header providing app identity, no TopNav needed.
tsx'use client';import {XDSAppShell} from '@xds/core/AppShell';import {XDSVStack} from '@xds/core/Stack';import {XDSHeading, XDSText} from '@xds/core/Text';import {XDSNavIcon} from '@xds/core/NavIcon';import {XDSSideNav,XDSSideNavHeading,XDSSideNavItem,XDSSideNavSection,} from '@xds/core/SideNav';import {ChartBarIcon,FolderIcon,UsersIcon,Cog6ToothIcon,} from '@heroicons/react/24/outline';import {HomeIcon} from '@heroicons/react/24/solid';import {CubeIcon} from '@heroicons/react/24/outline';import stylex from '@stylexjs/stylex';const styles = stylex.create({fit: {height: '100%',minHeight: 0,},});export default function AppShellSideNavOnly() {return (<XDSAppShellcontentPadding={6}xstyle={styles.fit}sideNav={<XDSSideNavheader={<XDSSideNavHeadingicon={<XDSNavIconicon={<CubeIcon style={{width: 16, height: 16}} />}/>}heading="App Shell"headingHref="#"/>}><XDSSideNavSection title="Main" isHeaderHidden><XDSSideNavItemlabel="Dashboard"icon={HomeIcon}isSelectedhref="#"/><XDSSideNavItem label="Analytics" icon={ChartBarIcon} href="#" /><XDSSideNavItem label="Projects" icon={FolderIcon} href="#" /></XDSSideNavSection><XDSSideNavSection title="Organization"><XDSSideNavItem label="Team" icon={UsersIcon} href="#" /><XDSSideNavItem label="Settings" icon={Cog6ToothIcon} href="#" /></XDSSideNavSection></XDSSideNav>}><XDSVStack gap={4}><XDSHeading level={3}>Page Content</XDSHeading><XDSText type="body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim adminim veniam, quis nostrud exercitation ullamco laboris.</XDSText></XDSVStack></XDSAppShell>);}
AppShell — Top Nav OnlySimple layout with TopNav and no side navigation, suitable for landing pages.
tsx'use client';import {XDSAppShell} from '@xds/core/AppShell';import {XDSVStack} from '@xds/core/Stack';import {XDSHeading, XDSText} from '@xds/core/Text';import {XDSTopNav, XDSTopNavHeading, XDSTopNavItem} from '@xds/core/TopNav';import {XDSNavIcon} from '@xds/core/NavIcon';import {CubeIcon} from '@heroicons/react/24/outline';import stylex from '@stylexjs/stylex';const styles = stylex.create({fit: {height: '100%',minHeight: 0,},});export default function AppShellTopNavOnly() {return (<XDSAppShellcontentPadding={6}xstyle={styles.fit}topNav={<XDSTopNavlabel="Main navigation"heading={<XDSTopNavHeadingheading="App Shell"logo={<XDSNavIconicon={<CubeIcon style={{width: 16, height: 16}} />}/>}/>}startContent={<><XDSTopNavItem label="Home" href="#" isSelected /><XDSTopNavItem label="Products" href="#" /><XDSTopNavItem label="Docs" href="#" /></>}/>}><XDSVStack gap={4}><XDSHeading level={3}>Page Content</XDSHeading><XDSText type="body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim adminim veniam, quis nostrud exercitation ullamco laboris.</XDSText></XDSVStack></XDSAppShell>);}
AppShell — Top Nav with Side NavThe most common layout with TopNav for app identity and SideNav for page-level navigation.
tsx'use client';import {XDSAppShell} from '@xds/core/AppShell';import {XDSVStack} from '@xds/core/Stack';import {XDSHeading, XDSText} from '@xds/core/Text';import {XDSTopNav, XDSTopNavHeading, XDSTopNavItem} from '@xds/core/TopNav';import {XDSNavIcon} from '@xds/core/NavIcon';import {XDSSideNav, XDSSideNavItem, XDSSideNavSection} from '@xds/core/SideNav';import {ChartBarIcon,FolderIcon,UsersIcon,Cog6ToothIcon,} from '@heroicons/react/24/outline';import {HomeIcon} from '@heroicons/react/24/solid';import {CubeIcon} from '@heroicons/react/24/outline';import stylex from '@stylexjs/stylex';const styles = stylex.create({fit: {height: '100%',minHeight: 0,},});export default function AppShellTopNavWithSideNav() {return (<XDSAppShellcontentPadding={6}xstyle={styles.fit}topNav={<XDSTopNavlabel="Main navigation"heading={<XDSTopNavHeadingheading="App Shell"logo={<XDSNavIconicon={<CubeIcon style={{width: 16, height: 16}} />}/>}/>}startContent={<><XDSTopNavItem label="Home" href="#" isSelected /><XDSTopNavItem label="Products" href="#" /><XDSTopNavItem label="Docs" href="#" /></>}/>}sideNav={<XDSSideNav><XDSSideNavSection title="Main" isHeaderHidden><XDSSideNavItemlabel="Dashboard"icon={HomeIcon}isSelectedhref="#"/><XDSSideNavItem label="Analytics" icon={ChartBarIcon} href="#" /><XDSSideNavItem label="Projects" icon={FolderIcon} href="#" /></XDSSideNavSection><XDSSideNavSection title="Organization"><XDSSideNavItem label="Team" icon={UsersIcon} href="#" /><XDSSideNavItem label="Settings" icon={Cog6ToothIcon} href="#" /></XDSSideNavSection></XDSSideNav>}><XDSVStack gap={4}><XDSHeading level={3}>Page Content</XDSHeading><XDSText type="body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim adminim veniam, quis nostrud exercitation ullamco laboris.</XDSText></XDSVStack></XDSAppShell>);}
AppShell — With BannerFull layout with TopNav, SideNav, and a dismissable info banner between the nav and content.
tsx'use client';import {XDSAppShell} from '@xds/core/AppShell';import {XDSBanner} from '@xds/core/Banner';import {XDSVStack} from '@xds/core/Stack';import {XDSHeading, XDSText} from '@xds/core/Text';import {XDSTopNav, XDSTopNavHeading, XDSTopNavItem} from '@xds/core/TopNav';import {XDSNavIcon} from '@xds/core/NavIcon';import {XDSSideNav, XDSSideNavItem, XDSSideNavSection} from '@xds/core/SideNav';import {ChartBarIcon,FolderIcon,UsersIcon,Cog6ToothIcon,} from '@heroicons/react/24/outline';import {HomeIcon} from '@heroicons/react/24/solid';import {CubeIcon} from '@heroicons/react/24/outline';import stylex from '@stylexjs/stylex';const styles = stylex.create({fit: {height: '100%',minHeight: 0,},});export default function AppShellWithBanner() {return (<XDSAppShellcontentPadding={6}xstyle={styles.fit}topNav={<XDSTopNavlabel="Main navigation"heading={<XDSTopNavHeadingheading="App Shell"logo={<XDSNavIconicon={<CubeIcon style={{width: 16, height: 16}} />}/>}/>}startContent={<><XDSTopNavItem label="Home" href="#" isSelected /><XDSTopNavItem label="Products" href="#" /><XDSTopNavItem label="Docs" href="#" /></>}/>}sideNav={<XDSSideNav><XDSSideNavSection title="Main" isHeaderHidden><XDSSideNavItemlabel="Dashboard"icon={HomeIcon}isSelectedhref="#"/><XDSSideNavItem label="Analytics" icon={ChartBarIcon} href="#" /><XDSSideNavItem label="Projects" icon={FolderIcon} href="#" /></XDSSideNavSection><XDSSideNavSection title="Organization"><XDSSideNavItem label="Team" icon={UsersIcon} href="#" /><XDSSideNavItem label="Settings" icon={Cog6ToothIcon} href="#" /></XDSSideNavSection></XDSSideNav>}banner={<XDSBannerstatus="info"container="section"title="System maintenance scheduled"description="The system will undergo maintenance tonight at 10pm UTC."isDismissable/>}><XDSVStack gap={4}><XDSHeading level={3}>Page Content</XDSHeading><XDSText type="body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim adminim veniam, quis nostrud exercitation ullamco laboris.</XDSText></XDSVStack></XDSAppShell>);}
Showcase source
tsx'use client';import {XDSAppShell} from '@xds/core/AppShell';import {XDSVStack} from '@xds/core/Stack';import {XDSHeading, XDSText} from '@xds/core/Text';import {XDSNavIcon} from '@xds/core/NavIcon';import {XDSSideNav,XDSSideNavHeading,XDSSideNavItem,XDSSideNavSection,} from '@xds/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';import stylex from '@stylexjs/stylex';const styles = stylex.create({fit: {height: '100%',minHeight: 0,width: '100%',},});export default function AppShellShowcase() {return (<XDSAppShellcontentPadding={6}xstyle={styles.fit}sideNav={<XDSSideNavheader={<XDSSideNavHeadingicon={<XDSNavIconicon={<CubeIcon style={{width: 16, height: 16}} />}/>}heading="App Shell"headingHref="#"/>}><XDSSideNavSection title="Main" isHeaderHidden><XDSSideNavItem label="Home" icon={HomeIcon} isSelected href="#" /><XDSSideNavItem label="Reports" icon={ChartBarIcon} href="#" /><XDSSideNavItemlabel="Documents"icon={DocumentTextIcon}href="#"/><XDSSideNavItem label="Team" icon={UsersIcon} href="#" /></XDSSideNavSection></XDSSideNav>}><XDSVStack gap={4}><XDSHeading level={3}>Page Content</XDSHeading><XDSText type="body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim adminim veniam, quis nostrud exercitation ullamco laboris.</XDSText></XDSVStack></XDSAppShell>);}