Skip to content
Containment

Resizable

A split view whose panels you drag, with a layout it remembers.

Walkthrough

1. Basic

A sidebar and its content. Drag the handle, or focus it and use the arrow keys; autoSaveId remembers the layout in localStorage, so a pane someone dragged is still there tomorrow.

Sidebar

Drag the handle

<script lang="ts">	import { ResizablePanelGroup, ResizablePanel, ResizableHandle, Text } from 'omaris';</script>​<div class="h-64 w-full overflow-hidden rounded-shape-lg border border-border">	<ResizablePanelGroup autoSaveId="docs-resizable-basic">		<ResizablePanel defaultSize={30} min={18}>			<div class="flex h-full items-center justify-center bg-surface-container-low">				<Text variant="body-sm" tone="muted">Sidebar</Text>			</div>		</ResizablePanel>		<ResizableHandle withHandle label="Resize sidebar" />		<ResizablePanel>			<div class="flex h-full items-center justify-center">				<Text variant="body-sm" tone="muted">Drag the handle</Text>			</div>		</ResizablePanel>	</ResizablePanelGroup></div>

2. Snapping and pixel limits

snapPoints pull the handle onto a share as it comes near — the line pulses as it lands — and min="200px" is a floor in pixels that holds however the group is resized. The readout is onLayout.

0%

snaps at 25 · 50 · 75, never under 200px

0%

takes whatever is left

<script lang="ts">	import { ResizablePanelGroup, ResizablePanel, ResizableHandle, Text } from 'omaris';​	let sizes = $state<number[]>([]);</script>​<div class="flex w-full flex-col gap-3">	<div class="h-56 w-full overflow-hidden rounded-shape-lg border border-border">		<ResizablePanelGroup bind:sizes>			<ResizablePanel defaultSize={40} min="200px" snapPoints={[25, 50, 75]}>				<div					class="flex h-full flex-col items-center justify-center gap-1 bg-surface-container-low"				>					<Text variant="title-md" tabular>{Math.round(sizes[0] ?? 0)}%</Text>					<Text variant="label-sm" tone="muted">snaps at 25 · 50 · 75, never under 200px</Text>				</div>			</ResizablePanel>			<ResizableHandle withHandle />			<ResizablePanel min={15}>				<div class="flex h-full flex-col items-center justify-center gap-1">					<Text variant="title-md" tabular>{Math.round(sizes[1] ?? 0)}%</Text>					<Text variant="label-sm" tone="muted">takes whatever is left</Text>				</div>			</ResizablePanel>		</ResizablePanelGroup>	</div>	<Text variant="body-sm" tone="muted" tabular>		{sizes.map((size) => `${size.toFixed(1)}%`).join(' · ')}	</Text></div>

3. Collapsible sidebar

bind:collapsed opens and shuts a collapsible panel from a toolbar button, and stays true to a drag that shuts it by hand. The group's reset() puts every panel back at its default.

Inbox

Folders

Messages

<script lang="ts">	import {		Button,		IconButton,		ResizablePanelGroup,		ResizablePanel,		ResizableHandle,		Text	} from 'omaris';​	let group = $state<ResizablePanelGroup>();	let collapsed = $state(false);</script>​<div class="flex w-full flex-col overflow-hidden rounded-shape-lg border border-border">	<div class="flex items-center gap-2 border-b border-border bg-surface-container-low px-2 py-1.5">		<IconButton			aria-label={collapsed ? 'Show sidebar' : 'Hide sidebar'}			aria-pressed={!collapsed}			onclick={() => (collapsed = !collapsed)}		>			<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">				<rect x="3" y="5" width="18" height="14" rx="2" />				<path d="M9 5v14" />			</svg>		</IconButton>		<Text variant="title-sm" class="flex-1">Inbox</Text>		<Button variant="text" size="xs" onclick={() => group?.reset()}>Reset layout</Button>	</div>	<div class="h-56 w-full">		<ResizablePanelGroup bind:this={group}>			<ResizablePanel defaultSize={28} min={18} collapsible bind:collapsed>				<div class="flex h-full items-center justify-center bg-surface-container-low">					<Text variant="body-sm" tone="muted">Folders</Text>				</div>			</ResizablePanel>			<ResizableHandle withHandle label="Resize sidebar" />			<ResizablePanel>				<div class="flex h-full items-center justify-center">					<Text variant="body-sm" tone="muted">						{collapsed ? 'Sidebar hidden — drag the edge to bring it back' : 'Messages'}					</Text>				</div>			</ResizablePanel>		</ResizablePanelGroup>	</div></div>

4. Nested

Groups nest, so a three-pane inbox is two groups and nothing else.

Folders

Message list

Preview

<script lang="ts">	import { ResizablePanelGroup, ResizablePanel, ResizableHandle, Text } from 'omaris';</script>​<div class="h-72 w-full overflow-hidden rounded-shape-lg border border-border">	<ResizablePanelGroup>		<ResizablePanel defaultSize={28} min={20}>			<div class="flex h-full items-center justify-center bg-surface-container-low">				<Text variant="body-sm" tone="muted">Folders</Text>			</div>		</ResizablePanel>		<ResizableHandle withHandle />		<ResizablePanel>			<ResizablePanelGroup direction="vertical">				<ResizablePanel defaultSize={45}>					<div class="flex h-full items-center justify-center">						<Text variant="body-sm" tone="muted">Message list</Text>					</div>				</ResizablePanel>				<ResizableHandle withHandle />				<ResizablePanel>					<div class="flex h-full items-center justify-center bg-surface-container-low">						<Text variant="body-sm" tone="muted">Preview</Text>					</div>				</ResizablePanel>			</ResizablePanelGroup>		</ResizablePanel>	</ResizablePanelGroup></div>

5. Vertical and persisted

direction="vertical" stacks the panels; the handle turns with them and the up and down arrows drive it. autoSaveId keeps this split across reloads, and onLayout reports every settled layout.

Editor

Terminal

onLayout → …

<script lang="ts">	import { ResizablePanelGroup, ResizablePanel, ResizableHandle, Text } from 'omaris';​	let sizes = $state<number[]>([]);</script>​<div class="flex w-full flex-col gap-3">	<div class="h-72 w-full overflow-hidden rounded-shape-lg border border-border">		<ResizablePanelGroup			direction="vertical"			autoSaveId="docs-resizable-vertical"			onLayout={(next) => (sizes = next)}		>			<ResizablePanel defaultSize={60} min={20}>				<div class="flex h-full items-center justify-center">					<Text variant="body-sm" tone="muted">Editor</Text>				</div>			</ResizablePanel>			<ResizableHandle withHandle label="Resize terminal" />			<ResizablePanel min={15} collapsible collapsedSize={0}>				<div class="flex h-full items-center justify-center bg-surface-container-low">					<Text variant="body-sm" tone="muted">Terminal</Text>				</div>			</ResizablePanel>		</ResizablePanelGroup>	</div>	<Text variant="body-sm" tone="muted" tabular>		onLayout → {sizes.map((size) => `${size.toFixed(1)}%`).join(' / ') || '…'}	</Text></div>

6. Overridden

Every part is reachable. hitArea={16} widens the pointer target without touching the line, classes restyles the line and the grip, and a disabled group keeps its layout but nothing in it moves.

Wide hit area

Restyled line and grip

Disabled group

Nothing here moves

<script lang="ts">	import { ResizablePanelGroup, ResizablePanel, ResizableHandle, Text } from 'omaris';</script>​<div class="flex w-full flex-col gap-4">	<div class="h-48 w-full overflow-hidden rounded-shape-lg border border-border">		<ResizablePanelGroup>			<ResizablePanel defaultSize={35} min={20}>				<div class="flex h-full items-center justify-center bg-surface-container-low">					<Text variant="body-sm" tone="muted">Wide hit area</Text>				</div>			</ResizablePanel>			<ResizableHandle				withHandle				hitArea={16}				classes={{					root: 'bg-surface-container after:bg-tertiary/40 hover:after:bg-tertiary data-dragging:after:bg-tertiary',					grip: 'h-12 w-2 bg-tertiary/60 group-hover/handle:bg-tertiary group-data-dragging/handle:bg-tertiary'				}}			/>			<ResizablePanel>				<div class="flex h-full items-center justify-center">					<Text variant="body-sm" tone="muted">Restyled line and grip</Text>				</div>			</ResizablePanel>		</ResizablePanelGroup>	</div>​	<div class="h-32 w-full overflow-hidden rounded-shape-lg border border-border">		<ResizablePanelGroup disabled sizes={[40, 60]}>			<ResizablePanel>				<div class="flex h-full items-center justify-center bg-surface-container-low">					<Text variant="body-sm" tone="muted">Disabled group</Text>				</div>			</ResizablePanel>			<ResizableHandle withHandle />			<ResizablePanel>				<div class="flex h-full items-center justify-center">					<Text variant="body-sm" tone="muted">Nothing here moves</Text>				</div>			</ResizablePanel>		</ResizablePanelGroup>	</div></div>