Files
Drag the line
Editor
Or use the arrow keys
Terminal
Panels can be nested
import {
ResizableHandle,
ResizablePanel,When to use it#
Use resizable panels for a workspace where people decide how much room each part gets: a file list beside an editor, a message list beside a conversation, a map above a table. The layout is theirs to adjust, so it suits tools that are used for a long time.
Do not use it on a page that people read once. A fixed layout is simpler and has fewer ways to go wrong. For a panel that opens and closes without a size, use a Drawer or a Collapsible.
It is built on react-resizable-panels.
Import#
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
usePanelRef,
} from "@hel/ui/resizable"Anatomy#
A ResizablePanelGroup holds the ResizablePanels, with a ResizableHandle between each pair. The group fills the width and height of its parent, so the parent needs a height of its own, such as h-72. A height set on the group itself is ignored, because the library sets it inline. Put a group inside a panel to nest them, as the preview does with the editor and the terminal.
<Stack className="h-72">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="30%" minSize="15%">...</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel>...</ResizablePanel>
</ResizablePanelGroup>
</Stack>Sizes are percentages when they are strings ("30%"), and pixels when they are numbers (240). Any CSS unit as a string also works, such as "16rem". Give each panel a minSize, so people cannot drag one down to nothing by accident.
A handle is a separator that people can also reach with the keyboard. Focus it, and the arrow keys move it, Home and End jump it to the smallest and largest size. Under a right-to-left direction, the horizontal panels mirror, so the first panel is on the right.
Examples#
Collapsible panel#
Mark a panel collapsible with a collapsedSize, and dragging past its minimum snaps it shut. Get a handle on it with usePanelRef to open and close it from a button, and use onResize to keep the label of the button right.
Conversations
Drag the line all the way left to collapse this panel.
Pick a conversation to read it here.
import * as React from "react"
import { Button } from "@hel/ui/button"
import {Remembering the layout#
To keep the sizes between visits, use useDefaultLayout from react-resizable-panels with the id of the group, and pass what it returns to the group as defaultLayout and onLayoutChanged. It can save to localStorage or to a cookie, which is better if the page is rendered on the server, because the first render already has the right sizes.
Props#
ResizablePanelGroup#
| Prop | Type | Default |
|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" |
bordered | boolean | false |
defaultLayout | the sizes by panel id | none |
onLayoutChanged | (layout) => void | none |
disabled | boolean | false |
bordered draws a rounded line around the group, for a layout that sits inside a page.
ResizablePanel#
| Prop | Type | Default |
|---|---|---|
defaultSize | number | string | an equal share |
minSize / maxSize | number | string | 0 / 100% |
collapsible | boolean | false |
collapsedSize | number | string | 0 |
onResize | (size) => void | none |
panelRef | from usePanelRef | none |
padding | a spacing step | "none" |
The content of a panel is a column, and it scrolls when it is taller than the panel. The handle from usePanelRef has collapse(), expand(), isCollapsed(), resize(size) and getSize().
ResizableHandle#
| Prop | Type | Default |
|---|---|---|
withHandle | boolean | false |
disabled | boolean | false |
withHandle draws a small grip on the line, so it is easy to see. The label that screen readers read is "Resize", and you can translate it with messages in the config.