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>

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>

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
classNamestringAdditional CSS class names applied to the root element.
contentReactNodeMain content slot.
data-testidstringTest ID applied to the root element.
endReactNodeEnd panel slot.
footerReactNodeFooter slot.
hasDividersbooleanWhether 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.
headerReactNodeHeader slot.
height"fill" | "auto"Layout height behavior. Default is fill.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10Outer padding for layout edges.
refRef<HTMLDivElement>Ref forwarded to the root element.
startReactNodeStart panel slot.
styleCSSPropertiesInline 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
asElementTypeHTML element to render. Default is div.
data-testidstringTest ID applied to the root element.
isScrollablebooleanWhether the content area scrolls when it overflows.
labelstringAccessible label. Automatically sets role="region" when provided.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10Inner padding.

Footer landmark region within a Layout.

When Variant 1

PropTypeDefaultDescription
childrennever
primaryButtonReactNodePrimary action button, rendered rightmost.
secondaryButtonReactNodeSecondary action button, rendered left of the primary button.
startContentReactNodeContent rendered at the start (left) of the footer.
classNamestringAdditional CSS class names applied to the footer.
data-testidstringTest ID applied to the root element.
heightnumber | stringFixed height for the footer.
labelstringAccessible label for the footer landmark.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10Inner padding.
refRef<HTMLElement>Ref forwarded to the footer element.
styleCSSPropertiesInline styles applied to the footer.

When Variant 2

PropTypeDefaultDescription
children*ReactNodeCustom footer content rendered inside the footer shell.
primaryButtonnever
secondaryButtonnever
startContentnever
classNamestringAdditional CSS class names applied to the footer.
data-testidstringTest ID applied to the root element.
heightnumber | stringFixed height for the footer.
labelstringAccessible label for the footer landmark.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10Inner padding.
refRef<HTMLElement>Ref forwarded to the footer element.
styleCSSPropertiesInline 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, a close button is automatically appended after `endContent`. The button calls `onOpenChange(false)` on the parent Dialog and the title receives initial focus.

PropTypeDefaultDescription
align'start' | 'center' | 'end'Cross-axis alignment of the start content, title, and end content within the header row. Default is start.
classNamestringAdditional CSS class names applied to the header.
data-testidstringTest ID applied to the header.
endContentReactNodeContent rendered after the title area.
heightnumber | stringFixed height for the header.
labelstringAccessible 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 | 10Inner padding.
refRef<HTMLElement>Ref forwarded to the header element.
startContentReactNodeContent rendered before the title area.
styleCSSPropertiesInline styles applied to the header.
subtitlestringSupporting text displayed below the title.
title*stringPrimary header title.

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

PropTypeDefaultDescription
data-testidstringTest ID applied to the root element.
isScrollablebooleanWhether the panel scrolls when it overflows.
labelstringAccessible label. Automatically sets role="region" when provided.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10Inner padding.
widthnumber | stringFixed width for the panel.