Layout
Shell with header, side panels, content, and footer slots.
Examples
Section titled “Examples”Header
<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>Content Only
Section titled “Content Only”<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>Auto Height
Section titled “Auto Height”Header
<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>With Padding
Section titled “With Padding”Header
<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>With End Panel
Section titled “With 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>With Footer
Section titled “With Footer”Header
<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>Custom Footer
Section titled “Custom Footer”Header
<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>All Slots
Section titled “All Slots”Header
<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>Panel Width
Section titled “Panel Width”<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>No Dividers
Section titled “No Dividers”Header
<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>Region Padding
Section titled “Region Padding”With dividers
Without dividers
<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>Non Scrollable Content
Section titled “Non Scrollable Content”Header
<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>Content Padding
Section titled “Content Padding”Header (default padding)
<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>Header Subtitle
Section titled “Header Subtitle”Settings
Manage your account preferences
<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>Header Level
Section titled “Header Level”Account settings
<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>Header Actions
Section titled “Header Actions”Profile
Edit details
<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>Header Height
Section titled “Header Height”Tall header
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>Layout
Section titled “Layout”Shell with header, side panels, content, and footer slots.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS class names applied to the root element. |
content | ReactNode | — | Main content slot. |
data-testid | string | — | Test ID applied to the root element. |
end | ReactNode | — | End panel slot. |
footer | ReactNode | — | Footer slot. |
hasDividers | boolean | — | 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. |
header | ReactNode | — | Header slot. |
height | "fill" | "auto" | — | Layout height behavior. Default is fill. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | — | Outer padding for layout edges. |
ref | Ref<HTMLDivElement> | — | Ref forwarded to the root element. |
start | ReactNode | — | Start panel slot. |
style | CSSProperties | — | Inline styles applied to the root element. |
LayoutContent
Section titled “LayoutContent”Scrollable main content area within a Layout. Fills the remaining space between panels and stretches to fill the available height.
| Prop | Type | Default | Description |
|---|---|---|---|
as | ElementType | — | HTML element to render. Default is div. |
data-testid | string | — | Test ID applied to the root element. |
isScrollable | boolean | — | Whether the content area scrolls when it overflows. |
label | string | — | Accessible label. Automatically sets role="region" when provided. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | — | Inner padding. |
LayoutFooter
Section titled “LayoutFooter”Footer landmark region within a Layout.
When Variant 1
| Prop | Type | Default | Description |
|---|---|---|---|
children | never | — | |
primaryButton | ReactNode | — | Primary action button, rendered rightmost. |
secondaryButton | ReactNode | — | Secondary action button, rendered left of the primary button. |
startContent | ReactNode | — | Content rendered at the start (left) of the footer. |
className | string | — | Additional CSS class names applied to the footer. |
data-testid | string | — | Test ID applied to the root element. |
height | number | string | — | Fixed height for the footer. |
label | string | — | Accessible label for the footer landmark. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | — | Inner padding. |
ref | Ref<HTMLElement> | — | Ref forwarded to the footer element. |
style | CSSProperties | — | Inline styles applied to the footer. |
When Variant 2
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | — | Custom footer content rendered inside the footer shell. |
primaryButton | never | — | |
secondaryButton | never | — | |
startContent | never | — | |
className | string | — | Additional CSS class names applied to the footer. |
data-testid | string | — | Test ID applied to the root element. |
height | number | string | — | Fixed height for the footer. |
label | string | — | Accessible label for the footer landmark. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | — | Inner padding. |
ref | Ref<HTMLElement> | — | Ref forwarded to the footer element. |
style | CSSProperties | — | Inline styles applied to the footer. |
LayoutHeader
Section titled “LayoutHeader”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.
| Prop | Type | Default | Description |
|---|---|---|---|
align | 'start' | 'center' | 'end' | — | Cross-axis alignment of the start content, title, and end content within the header row. Default is start. |
className | string | — | Additional CSS class names applied to the header. |
data-testid | string | — | Test ID applied to the header. |
endContent | ReactNode | — | Content rendered after the title area. |
height | number | string | — | Fixed height for the header. |
label | string | — | Accessible label for the header landmark. |
level | 1 | 2 | 3 | 4 | 5 | 6 | 4 | Semantic heading level used for the title. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | — | Inner padding. |
ref | Ref<HTMLElement> | — | Ref forwarded to the header element. |
startContent | ReactNode | — | Content rendered before the title area. |
style | CSSProperties | — | Inline styles applied to the header. |
subtitle | string | — | Supporting text displayed below the title. |
title* | string | — | Primary header title. |
LayoutPanel
Section titled “LayoutPanel”Side panel region within a Layout. Placed in the start or end slot, with optional dividers and scrolling.
| Prop | Type | Default | Description |
|---|---|---|---|
data-testid | string | — | Test ID applied to the root element. |
isScrollable | boolean | — | Whether the panel scrolls when it overflows. |
label | string | — | Accessible label. Automatically sets role="region" when provided. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | — | Inner padding. |
width | number | string | — | Fixed width for the panel. |