XDSAppShell@xds/core · AppShell
Preview coming soon

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

GuidancePractices
DoChoose the right height — use "fill" for dashboards with internal scrolling and "auto" for pages that grow with content.
DoSet `contentPadding` based on content type — 4 for forms and settings, 0 for tables and dashboards.
Don'tNest one AppShell inside another — it's the outermost layout frame.
Don'tUse for sub-page layouts — use Layout for content areas within AppShell.

Import

ts
import {XDSAppShell} from '@xds/core/AppShell'

Props

PropTypeDescription
children
ReactNodeMain 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
ReactNodeTop navigation slot, typically XDSTopNav.
sideNav
ReactNodeSide navigation slot, typically XDSSideNav.
mobileNav
ReactNodeMobile navigation configuration. Accepts false (disable), config object (tune auto behavior), or ReactNode (full custom drawer).
banner
ReactNodeBanner 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
booleanWhether the sideNav is collapsed (controlled mode).
defaultIsSideNavCollapsed
boolean (default: false)Initial collapsed state for uncontrolled mode.
onSideNavCollapsedChange
(isCollapsed: boolean) => voidCallback 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
StyleXStylesStyleX 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 do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim 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 (
<XDSAppShell
contentPadding={6}
xstyle={styles.fit}
sideNav={
<XDSSideNav
header={
<XDSSideNavHeading
icon={
<XDSNavIcon
icon={<CubeIcon style={{width: 16, height: 16}} />}
/>
}
heading="App Shell"
headingHref="#"
/>
}>
<XDSSideNavSection title="Main" isHeaderHidden>
<XDSSideNavItem
label="Dashboard"
icon={HomeIcon}
isSelected
href="#"
/>
<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 do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim 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 (
<XDSAppShell
contentPadding={6}
xstyle={styles.fit}
topNav={
<XDSTopNav
label="Main navigation"
heading={
<XDSTopNavHeading
heading="App Shell"
logo={
<XDSNavIcon
icon={<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 do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim 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 (
<XDSAppShell
contentPadding={6}
xstyle={styles.fit}
topNav={
<XDSTopNav
label="Main navigation"
heading={
<XDSTopNavHeading
heading="App Shell"
logo={
<XDSNavIcon
icon={<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>
<XDSSideNavItem
label="Dashboard"
icon={HomeIcon}
isSelected
href="#"
/>
<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 do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim 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 (
<XDSAppShell
contentPadding={6}
xstyle={styles.fit}
topNav={
<XDSTopNav
label="Main navigation"
heading={
<XDSTopNavHeading
heading="App Shell"
logo={
<XDSNavIcon
icon={<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>
<XDSSideNavItem
label="Dashboard"
icon={HomeIcon}
isSelected
href="#"
/>
<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={
<XDSBanner
status="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 do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim 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 (
<XDSAppShell
contentPadding={6}
xstyle={styles.fit}
sideNav={
<XDSSideNav
header={
<XDSSideNavHeading
icon={
<XDSNavIcon
icon={<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="#" />
<XDSSideNavItem
label="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 do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris.
</XDSText>
</XDSVStack>
</XDSAppShell>
);
}