Theme
Customize appearance
Components
Resizable
Let users adjust the space allocated to neighboring panels.
View component source ↗Preview
A minimal example to get started.
Left
Right
Installation
Install this component and its dependencies in a project initialized with shadcn.
pnpm dlx shadcn@latest add https://ui.hyunjoon.net/r/resizable.jsonUsage
This is the basic example. Use the Code tab above for the selected example. Adjust import aliases to match your project.
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
} from "@/components/ui/resizable";
export default function Example() {
return (
<ResizablePanelGroup orientation="horizontal" className="min-h-40 max-w-sm rounded-lg border">
<ResizablePanel defaultSize="50%">
<div className="flex h-full items-center justify-center p-4">Left</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="50%">
<div className="flex h-full items-center justify-center p-4">Right</div>
</ResizablePanel>
</ResizablePanelGroup>
);
}
API reference
Derived from the installed component types. Native DOM props are also supported where applicable. A dash means no explicit default is documented. Some props depend on the selected mode.
ResizableHandle
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | undefinedstring | undefined | — | CSS class name. ℹ️ Use the `data-separator` attribute for custom _hover_ and _active_ styles ⚠️ The following properties cannot be overridden: `flex-grow`, `flex-shrink` |
defaultValue | string | number | readonly string[] | undefinedstring | number | readonly string[] | undefined | — | Initial value for an uncontrolled component. |
defaultChecked | boolean | undefinedboolean | undefined | — | Initial checked state for an uncontrolled control. |
style | React.CSSProperties | undefinedReact.CSSProperties | undefined | — | CSS properties. ℹ️ Use the `data-separator` attribute for custom _hover_ and _active_ styles ⚠️ The following properties cannot be overridden: `flex-grow`, `flex-shrink` |
id | string | undefinedstring | undefined | — | Uniquely identifies the separator within the parent group. Falls back to `useId` when not provided. ℹ️ This value will also be assigned to the `data-separator` attribute. |
children | React.ReactNodeReact.ReactNode | — | Content rendered inside this component. |
onFocus | React.FocusEventHandler<HTMLDivElement> | undefinedReact.FocusEventHandler<HTMLDivElement> | undefined | — | Called when the control receives focus. |
onBlur | React.FocusEventHandler<HTMLDivElement> | undefinedReact.FocusEventHandler<HTMLDivElement> | undefined | — | Called when the control loses focus. |
onChange | React.ChangeEventHandler<HTMLDivElement, Element> | undefinedReact.ChangeEventHandler<HTMLDivElement, Element> | undefined | — | Called when the underlying form control changes; inspect the event for its value. |
onKeyDown | React.KeyboardEventHandler<HTMLDivElement> | undefinedReact.KeyboardEventHandler<HTMLDivElement> | undefined | — | Called when a key is pressed while this element is focused. |
onClick | React.MouseEventHandler<HTMLDivElement> | undefinedReact.MouseEventHandler<HTMLDivElement> | undefined | — | Called when the element is activated. |
disabled | boolean | undefinedboolean | undefined | — | When disabled, the separator cannot be used to resize its neighboring panels. ℹ️ The panels may still be resized indirectly (while other panels are being resized). To prevent a panel from being resized at all, it needs to also be disabled. |
disableDoubleClick | boolean | undefinedboolean | undefined | — | When true, double-clicking this `Separator` will not reset its `Panel` to its default size. |
elementRef | React.Ref<HTMLDivElement> | undefinedReact.Ref<HTMLDivElement> | undefined | — | Ref attached to the root `HTMLDivElement`. |
withHandle | boolean | undefinedboolean | undefined | — | Displays a visible grip on the resize handle. |
ResizablePanel
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | undefinedstring | undefined | — | CSS class name. ⚠️ Class is applied to nested `HTMLDivElement` to avoid styles that interfere with Flex layout. |
defaultValue | string | number | readonly string[] | undefinedstring | number | readonly string[] | undefined | — | Initial value for an uncontrolled component. |
defaultChecked | boolean | undefinedboolean | undefined | — | Initial checked state for an uncontrolled control. |
style | React.CSSProperties | undefinedReact.CSSProperties | undefined | — | CSS properties. ⚠️ Style is applied to nested `HTMLDivElement` to avoid styles that interfere with Flex layout. |
id | string | undefinedstring | undefined | — | Uniquely identifies this panel within the parent group. Falls back to `useId` when not provided. ℹ️ This prop is used to associate persisted group layouts with the original panel. ℹ️ This value will also be assigned to the `data-panel` attribute. |
children | React.ReactNodeReact.ReactNode | — | Content rendered inside this component. |
onFocus | React.FocusEventHandler<HTMLDivElement> | undefinedReact.FocusEventHandler<HTMLDivElement> | undefined | — | Called when the control receives focus. |
onBlur | React.FocusEventHandler<HTMLDivElement> | undefinedReact.FocusEventHandler<HTMLDivElement> | undefined | — | Called when the control loses focus. |
onChange | React.ChangeEventHandler<HTMLDivElement, Element> | undefinedReact.ChangeEventHandler<HTMLDivElement, Element> | undefined | — | Called when the underlying form control changes; inspect the event for its value. |
onKeyDown | React.KeyboardEventHandler<HTMLDivElement> | undefinedReact.KeyboardEventHandler<HTMLDivElement> | undefined | — | Called when a key is pressed while this element is focused. |
onClick | React.MouseEventHandler<HTMLDivElement> | undefinedReact.MouseEventHandler<HTMLDivElement> | undefined | — | Called when the element is activated. |
collapsedSize | string | number | undefinedstring | number | undefined | — | Panel size when collapsed; defaults to 0%. |
collapsible | boolean | undefinedboolean | undefined | — | This panel can be collapsed. ℹ️ A collapsible panel will collapse when it's size is less than of the specified `minSize` |
defaultSize | string | number | undefinedstring | number | undefined | — | Default size of Panel within its parent group; default is auto-assigned based on the total number of Panels. ℹ️ Interpretation rules: - Numbers are interpreted as pixels (e.g. `defaultSize={200}` is 200 pixels) - Strings without explicit units are interpreted as percentage (e.g. `defaultSize="50"` is 50 percent) - Use explicit units (e.g. "px", "%", "em", "rem", "vh", or "vw") to change interpretation ⚠️ Percentage based sizes may cause slight layout shift when server-rendering. For more information see the documentation. |
disabled | boolean | undefinedboolean | undefined | — | When disabled, a panel cannot be resized either directly or indirectly (by resizing another panel). |
elementRef | React.Ref<HTMLDivElement | null> | undefinedReact.Ref<HTMLDivElement | null> | undefined | — | Ref attached to the root `HTMLDivElement`. |
groupResizeBehavior | "preserve-relative-size" | "preserve-pixel-size" | undefined"preserve-relative-size" | "preserve-pixel-size" | undefined | — | How should this Panel behave if the parent Group is resized? Defaults to `preserve-relative-size`. - `preserve-relative-size`: Retain the current relative size (as a percentage of the Group) - `preserve-pixel-size`: Retain its current size (in pixels) ℹ️ Panel min/max size constraints may impact this behavior. ⚠️ A Group must contain at least one Panel with `preserve-relative-size` resize behavior. |
maxSize | string | number | undefinedstring | number | undefined | — | Maximum size of Panel within its parent group; defaults to `"100%"`. ℹ️ Interpretation rules: - Numbers are interpreted as pixels (e.g. `maxSize={200}` is 200 pixels) - Strings without explicit units are interpreted as percentage (e.g. `maxSize="50"` is 50 percent) - Use explicit units (e.g. "px", "%", "em", "rem", "vh", or "vw") to change interpretation |
minSize | string | number | undefinedstring | number | undefined | — | Minimum size of Panel within its parent group; defaults to 0%. ℹ️ Interpretation rules: - Numbers are interpreted as pixels (e.g. `minSize={200}` is 200 pixels) - Strings without explicit units are interpreted as percentage (e.g. `minSize="50"` is 50 percent) - Use explicit units (e.g. "px", "%", "em", "rem", "vh", or "vw") to change interpretation |
onResize | ((panelSize: ResizablePrimitive.PanelSize, id: string | number | undefined, prevPanelSize:…((panelSize: ResizablePrimitive.PanelSize, id: string | number | undefined, prevPanelSize: ResizablePrimitive.PanelSize | undefined) => void) | undefined | — | Called when panel sizes change. |
panelRef | React.Ref<ResizablePrimitive.PanelImperativeHandle | null> | undefinedReact.Ref<ResizablePrimitive.PanelImperativeHandle | null> | undefined | — | Exposes the following imperative API: - `collapse(): void` - `expand(): void` - `getSize(): number` - `isCollapsed(): boolean` - `resize(size: number): void` ℹ️ The `usePanelRef` and `usePanelCallbackRef` hooks are exported for convenience use in TypeScript projects. |
ResizablePanelGroup
| Prop | Type | Default | Description |
|---|---|---|---|
defaultChecked | boolean | undefinedboolean | undefined | — | Initial checked state for an uncontrolled control. |
defaultValue | string | number | readonly string[] | undefinedstring | number | readonly string[] | undefined | — | Initial value for an uncontrolled component. |
className | string | undefinedstring | undefined | — | CSS class name. |
id | string | undefinedstring | undefined | — | Uniquely identifies this group within an application. Falls back to `useId` when not provided. ℹ️ This value will also be assigned to the `data-group` attribute. |
style | React.CSSProperties | undefinedReact.CSSProperties | undefined | — | CSS properties. ⚠️ The following styles cannot be overridden: `display`, `flex-direction`, `flex-wrap`, and `overflow`. |
children | React.ReactNodeReact.ReactNode | — | Panel and Separator components that comprise this group. |
onFocus | React.FocusEventHandler<HTMLDivElement> | undefinedReact.FocusEventHandler<HTMLDivElement> | undefined | — | Called when the control receives focus. |
onBlur | React.FocusEventHandler<HTMLDivElement> | undefinedReact.FocusEventHandler<HTMLDivElement> | undefined | — | Called when the control loses focus. |
onChange | React.ChangeEventHandler<HTMLDivElement, Element> | undefinedReact.ChangeEventHandler<HTMLDivElement, Element> | undefined | — | Called when the underlying form control changes; inspect the event for its value. |
onKeyDown | React.KeyboardEventHandler<HTMLDivElement> | undefinedReact.KeyboardEventHandler<HTMLDivElement> | undefined | — | Called when a key is pressed while this element is focused. |
onClick | React.MouseEventHandler<HTMLDivElement> | undefinedReact.MouseEventHandler<HTMLDivElement> | undefined | — | Called when the element is activated. |
defaultLayout | ResizablePrimitive.Layout | undefinedResizablePrimitive.Layout | undefined | — | Default layout for the Group. ℹ️ This value allows layouts to be remembered between page reloads. ⚠️ Slight layout shift may occur when server-rendering panels with percentage-based default sizes. Refer to the documentation for suggestions on how to minimize the impact of this. |
disableCursor | boolean | undefinedboolean | undefined | — | This library sets custom mouse cursor styles to indicate drag state. Use this prop to disable that behavior for Panels and Separators in this group. |
disabled | boolean | undefinedboolean | undefined | — | Disable resize functionality. |
elementRef | React.Ref<HTMLDivElement | null> | undefinedReact.Ref<HTMLDivElement | null> | undefined | — | Ref attached to the root `HTMLDivElement`. |
groupRef | React.Ref<ResizablePrimitive.GroupImperativeHandle | null> | undefinedReact.Ref<ResizablePrimitive.GroupImperativeHandle | null> | undefined | — | Exposes the following imperative API: - `getLayout(): Layout` - `setLayout(layout: Layout): void` ℹ️ The `useGroupRef` and `useGroupCallbackRef` hooks are exported for convenience use in TypeScript projects. |
onLayoutChange | ((layout: ResizablePrimitive.Layout) => void | undefined) | undefined((layout: ResizablePrimitive.Layout) => void | undefined) | undefined | — | Called when the Group's layout is changing. ⚠️ For layout changes caused by pointer events, this method is called each time the pointer is moved. For most cases, it is recommended to use the `onLayoutChanged` callback instead. |
onLayoutChanged | ((layout: ResizablePrimitive.Layout, meta: ResizablePrimitive.LayoutChangedMeta) => void) …((layout: ResizablePrimitive.Layout, meta: ResizablePrimitive.LayoutChangedMeta) => void) | undefined | — | Called after the Group's layout has been changed. ℹ️ For layout changes caused by pointer events, this method is not called until the pointer has been released. This method is recommended when saving layouts to some storage api. ℹ️ The second argument contains meta information about the layout change. The `isUserInteraction` attribute signals whether the resize was caused by direct user input. It is true for resizes caused by pointer or keyboard input and false for other triggers (e.g. imperative API calls, initial mount, etc.) |
resizeTargetMinimumSize | { coarse: number; fine: number; } | undefined{ coarse: number; fine: number; } | undefined | — | Minimum size of the resizable hit target area (either `Separator` or `Panel` edge) This threshold ensures are large enough to avoid mis-clicks. - Coarse inputs (typically a finger on a touchscreen) have reduced accuracy; to ensure accessibility and ease of use, hit targets should be larger to prevent mis-clicks. - Fine inputs (typically a mouse) can be smaller ℹ️ [Apple interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility) suggest `20pt` (`27px`) on desktops and `28pt` (`37px`) for touch devices In practice this seems to be much larger than many of their own applications use though. |
orientation | "horizontal" | "vertical" | undefined"horizontal" | "vertical" | undefined | — | Specifies the resizable orientation ("horizontal" or "vertical"); defaults to "horizontal" |
Usage notes
Place handles between panels in a panel group. Give the container a useful size and set minimum panel sizes when content requires them.