Skip to content

Layout

Shell with header, side panels, content, and footer slots.

Header

Start panel
Main content
<div style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Main content</Text>
</LayoutContent>
}
header={<LayoutHeader title="Header" />}
start={
<LayoutPanel width={220}>
<Text type="body">Start panel</Text>
</LayoutPanel>
}
/>
</div>
This layout has only content, no header or panels.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">
This layout has only content, no header or panels.
</Text>
</LayoutContent>
}
hasDividers={false}
/>
</Card>

Header

Auto-height layout grows with content.No fixed height constraint.The layout is only as tall as needed.Additional content pushes the height further.This demonstrates natural document flow.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<VStack gap={2}>
<Text type="body">Auto-height layout grows with content.</Text>
<Text type="body">No fixed height constraint.</Text>
<Text type="body">The layout is only as tall as needed.</Text>
<Text type="body">
Additional content pushes the height further.
</Text>
<Text type="body">This demonstrates natural document flow.</Text>
</VStack>
</LayoutContent>
}
header={<LayoutHeader title="Header" />}
height="auto"
/>
</Card>

Header

Content with outer padding on the layout.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Content with outer padding on the layout.</Text>
</LayoutContent>
}
header={<LayoutHeader title="Header" />}
padding={4}
/>
</Card>
Start panel
Main content area
End panel
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Main content area</Text>
</LayoutContent>
}
end={
<LayoutPanel width={200}>
<Text type="body">End panel</Text>
</LayoutPanel>
}
start={
<LayoutPanel width={200}>
<Text type="body">Start panel</Text>
</LayoutPanel>
}
/>
</Card>

Header

Main content
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Main content</Text>
</LayoutContent>
}
footer={
<LayoutFooter
primaryButton={<Button label="Save" variant="primary" />}
secondaryButton={<Button label="Cancel" variant="ghost" />}
/>
}
header={<LayoutHeader title="Header" />}
/>
</Card>

Header

Main content
Autosaved 2 minutes ago
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Main content</Text>
</LayoutContent>
}
footer={
<LayoutFooter>
<div
style={{
alignItems: 'center',
display: 'flex',
gap: 12,
justifyContent: 'space-between',
}}>
<Text color="secondary" type="body">
Autosaved 2 minutes ago
</Text>
<div style={{display: 'flex', gap: 8}}>
<Button label="Discard" variant="ghost" />
<Button label="Publish" variant="primary" />
</div>
</div>
</LayoutFooter>
}
header={<LayoutHeader title="Header" />}
/>
</Card>

Header

Start panel
Main content
End panel
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Main content</Text>
</LayoutContent>
}
end={
<LayoutPanel width={180}>
<Text type="body">End panel</Text>
</LayoutPanel>
}
footer={
<LayoutFooter
primaryButton={<Button label="Save" variant="primary" />}
secondaryButton={<Button label="Cancel" variant="ghost" />}
/>
}
header={<LayoutHeader title="Header" />}
start={
<LayoutPanel width={180}>
<Text type="body">Start panel</Text>
</LayoutPanel>
}
/>
</Card>
200px panel
Start panel is 200px, end panel is 300px.
300px panel
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Start panel is 200px, end panel is 300px.</Text>
</LayoutContent>
}
end={
<LayoutPanel width={300}>
<Text type="body">300px panel</Text>
</LayoutPanel>
}
start={
<LayoutPanel width={200}>
<Text type="body">200px panel</Text>
</LayoutPanel>
}
/>
</Card>

Header

Start panel
No dividers on any region.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">No dividers on any region.</Text>
</LayoutContent>
}
hasDividers={false}
header={<LayoutHeader title="Header" />}
start={
<LayoutPanel width={200}>
<Text type="body">Start panel</Text>
</LayoutPanel>
}
/>
</Card>

With dividers

16px either side of each rule.

Without dividers

16px under the title, not 32px.
<HStack gap={4}>
<Card style={{height: 320, width: 280}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">16px either side of each rule.</Text>
</LayoutContent>
}
footer={<LayoutFooter primaryButton={<Button label="Save" />} />}
hasDividers
header={<LayoutHeader title="With dividers" />}
/>
</Card>
<Card style={{height: 320, width: 280}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">16px under the title, not 32px.</Text>
</LayoutContent>
}
footer={<LayoutFooter primaryButton={<Button label="Save" />} />}
hasDividers={false}
header={<LayoutHeader title="Without dividers" />}
/>
</Card>
</HStack>

Header

This content area does not scroll.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent isScrollable={false}>
<Text type="body">This content area does not scroll.</Text>
</LayoutContent>
}
header={<LayoutHeader title="Header" />}
/>
</Card>

Header (default padding)

This content has padding=8 for extra spacing.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent padding={8}>
<Text type="body">
This content has padding=8 for extra spacing.
</Text>
</LayoutContent>
}
header={<LayoutHeader title="Header (default padding)" />}
/>
</Card>

Settings

Manage your account preferences

Header with supporting subtitle text.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Header with supporting subtitle text.</Text>
</LayoutContent>
}
header={
<LayoutHeader
subtitle="Manage your account preferences"
title="Settings"
/>
}
/>
</Card>

Account settings

Header title renders as a level 2 heading in this layout.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">
Header title renders as a level 2 heading in this layout.
</Text>
</LayoutContent>
}
header={<LayoutHeader level={2} title="Account settings" />}
/>
</Card>

Account settings

Header title renders as an h2 but is announced as a level 4 heading.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">
Header title renders as an h2 but is announced as a level 4
heading.
</Text>
</LayoutContent>
}
header={
<LayoutHeader
accessibilityLevel={4}
level={2}
title="Account settings"
/>
}
/>
</Card>

Profile

Edit details

startContent holds a back button, endContent holds primary actions.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">
startContent holds a back button, endContent holds primary
actions.
</Text>
</LayoutContent>
}
header={
<LayoutHeader
endContent={<Button label="Save" variant="primary" />}
startContent={
<Button
icon={ChevronLeft}
isIconOnly
label="Back"
variant="ghost"
/>
}
subtitle="Edit details"
title="Profile"
/>
}
/>
</Card>

Projects

align="center" — recommended with action buttons

Projects

Projects assigned to you

align="center" with a subtitle

Projects

align="start" — the default

Projects

align="end"
<VStack gap={4}>
{(
[
['center', 'align="center" — recommended with action buttons'],
['center-subtitle', 'align="center" with a subtitle'],
['start', 'align="start" — the default'],
['end', 'align="end"'],
] as const
).map(([key, description]) => (
<Card key={key}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">{description}</Text>
</LayoutContent>
}
header={
<LayoutHeader
align={key === 'center-subtitle' ? 'center' : key}
endContent={
<>
<Button label="New Project" variant="secondary" />
<Button label="Share" variant="primary" />
</>
}
subtitle={
key === 'center-subtitle'
? 'Projects assigned to you'
: undefined
}
title="Projects"
/>
}
height="auto"
/>
</Card>
))}
</VStack>

Tall header

Fixed 96px height

Header is pinned to a fixed 96px height.
<Card style={{height: 420}}>
<Layout
{...args}
content={
<LayoutContent>
<Text type="body">Header is pinned to a fixed 96px height.</Text>
</LayoutContent>
}
header={
<LayoutHeader
height={96}
subtitle="Fixed 96px height"
title="Tall header"
/>
}
/>
</Card>

Shell with header, side panels, content, and footer slots.

PropTypeDefaultDescription
classNamestring—Additional CSS class names applied to the root element.
contentReactNode—Main content slot.
data-testidstring—Test ID applied to the root element.
endReactNode—End panel slot.
footerReactNode—Footer slot.
hasDividersboolean—Whether child layout regions should show dividers. Default is true. Without dividers the regions read as one surface, so the content region drops its block padding on the edges that meet a header or a footer rather than stacking a second padding against theirs.
headerReactNode—Header slot.
height"fill" | "auto"—Layout height behavior. Default is fill.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10—Outer padding for layout edges.
refRef<HTMLDivElement>—Ref forwarded to the root element.
startReactNode—Start panel slot.
styleCSSProperties—Inline styles applied to the root element.

Scrollable main content area within a Layout. Fills the remaining space between panels and stretches to fill the available height.

PropTypeDefaultDescription
asElementType—HTML element to render. Default is div.
data-testidstring—Test ID applied to the root element.
isScrollableboolean—Whether the content area scrolls when it overflows.
labelstring—Accessible label. Automatically sets role="region" when provided.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10—Inner padding.

Footer landmark region within a Layout.

When Variant 1

PropTypeDefaultDescription
childrennever—
primaryButtonReactNode—Primary action button, rendered rightmost.
secondaryButtonReactNode—Secondary action button, rendered left of the primary button.
startContentReactNode—Content rendered at the start (left) of the footer.
classNamestring—Additional CSS class names applied to the footer.
data-testidstring—Test ID applied to the root element.
heightSizeValue—Fixed height for the footer. Numbers are pixels, strings are used as-is.
labelstring—Accessible label for the footer landmark.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10—Inner padding.
refRef<HTMLElement>—Ref forwarded to the footer element.
styleCSSProperties—Inline styles applied to the footer.

When Variant 2

PropTypeDefaultDescription
children*ReactNode—Custom footer content rendered inside the footer shell.
primaryButtonnever—
secondaryButtonnever—
startContentnever—
classNamestring—Additional CSS class names applied to the footer.
data-testidstring—Test ID applied to the root element.
heightSizeValue—Fixed height for the footer. Numbers are pixels, strings are used as-is.
labelstring—Accessible label for the footer landmark.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10—Inner padding.
refRef<HTMLElement>—Ref forwarded to the footer element.
styleCSSProperties—Inline styles applied to the footer.

Header landmark region within a Layout with a structured title, optional subtitle, and start/end content slots. When rendered inside a Dialog, Drawer, or Popover, a close button is automatically appended after `endContent` unless `hasCloseButton` is false. The button calls `onOpenChange(false)` on the parent surface. Dialog and Drawer also use the title as their fallback initial focus target.

PropTypeDefaultDescription
accessibilityLevel1 | 2 | 3 | 4 | 5 | 6—Overrides the exposed heading level of the title. The rendered h1-h6 element follows this value while level keeps driving the typography.
align'start' | 'center' | 'end'—Cross-axis alignment of the start content, title, and end content within the header row. Default is start.
classNamestring—Additional CSS class names applied to the header.
data-testidstring—Test ID applied to the header.
endContentReactNode—Content rendered after the title area.
hasCloseButtonbooleantrueWhether to append the automatic close button inside an overlay.
heightSizeValue—Fixed height for the header. Numbers are pixels, strings are used as-is.
labelstring—Accessible label for the header landmark.
level1 | 2 | 3 | 4 | 5 | 64Semantic heading level used for the title.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10—Inner padding.
refRef<HTMLElement>—Ref forwarded to the header element.
startContentReactNode—Content rendered before the title area.
styleCSSProperties—Inline styles applied to the header.
subtitlestring—Supporting text displayed below the title.
title*string—Primary header title.

Side panel region within a Layout. Placed in the start or end slot, with optional dividers and scrolling.

PropTypeDefaultDescription
data-testidstring—Test ID applied to the root element.
isScrollableboolean—Whether the panel scrolls when it overflows.
labelstring—Accessible label. Automatically sets role="region" when provided.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10—Inner padding.
widthSizeValue—Fixed width for the panel. Numbers are pixels, strings are used as-is.