Usage
Hook for adding drag-to-resize behavior to layout regions. Supports single-region and multi-region configurations with snap points, collapsible panels, localStorage persistence, and cascade resize ordering.
tsimport {useResizable} from '@astryxdesign/core/Resizable'
Best practices
| Guidance | Practices |
|---|---|
| Do | Use percent(40, {min: pixel(333)}) for a 40% size with a 333px floor, or percent(10, {max: pixel(400)}) for a 10% size with a 400px ceiling. The options argument is required and carries a floor XOR a ceiling. |
| Do | A structured default is an initial choice only; a structured minSize or maxSize remains live. State, persistence, callbacks, resize(), paint, and ARIA all use resolved pixel numbers. |
| Do | Import percent and Table’s same pixel binding from @astryxdesign/core/Resizable/utils when constructing configuration in a Server Component; the root package exposes one pixel symbol and one percent symbol without collision. |
| Do | Use with Layout or AppShell sidebar for resizable navigation panels. |
| Do | Set autoSaveId to persist user-chosen sizes across page reloads. |
| Don't | Set minSize too small; content becomes unreadable. Prefer collapsible for panels that can hide entirely. |
Parameters
| Param | Type | Description |
|---|---|---|
defaultSize | | (default: 250) | Initial size. Numbers, exact "Npx", and pixel(value) are pixels. Exact "N%" has no additional pixel bound. percent(value, {min: pixel(value)}) or percent(value, {max: pixel(value)}) adds one pixel floor or ceiling. A percentage resolves ONCE into pixels — against containerRef when supplied, against the viewport otherwise — and does not track its basis afterwards. The released broad number | string type remains compatible; runtime validation is authoritative. |
minSize | (default: 50) | Minimum size. Numbers, exact "Npx", and pixel(value) remain pixels; exact "N%" has no additional pixel bound; percent(value, {min: pixel(value)}) or percent(value, {max: pixel(value)}) adds exactly one. Percentage minimums re-resolve when their basis changes and clamp the current pixel selection. |
maxSize | (default: Infinity) | Maximum size. Numbers, exact "Npx", and pixel(value) remain pixels; exact "N%" has no additional pixel bound; percent(value, {min: pixel(value)}) or percent(value, {max: pixel(value)}) adds exactly one. Percentage maximums re-resolve when their basis changes and clamp the current pixel selection. |
containerRef | RefObject<HTMLElement | null> | The element a percentage is a share of. Caller-owned: the hook never infers one. Omitted, percentages use the viewport, which is the released behaviour. The ref may point at a different element over time — the basis follows it. Until that element is actually laid out (not yet mounted, display:none, detached) percentages use a temporary 1200px basis rather than its zero measurement, and nothing is persisted from it. |
direction | 'horizontal' | 'vertical' (default: 'horizontal') | Which axis this region resizes along. Selects the container's inline or block content-box size as the percentage basis, and must match the direction given to ResizeHandle. |
minSizePx | number | Deprecated. Use minSize, which also accepts a percentage. Supplying both is a type error; if untyped code supplies both, minSize wins. |
maxSizePx | number | Deprecated. Use maxSize, which also accepts a percentage. Explicit Infinity remains valid. |
collapsible | boolean (default: false) | Whether dragging below the collapsed threshold collapses the region to zero. |
snaps | number[] | Pixel values to snap to during drag. |
autoSaveId | string | Key for localStorage persistence of size and collapse state across sessions. |
defaultIsCollapsed | boolean (default: false) | Initial collapse state (uncontrolled). A persisted entry wins over it. |
isCollapsed | boolean | Controlled collapse state. collapse(), expand() and a drag past the threshold then report through onCollapseChange instead of changing state internally. |
onCollapseChange | (isCollapsed: boolean) => void | Called once per collapse state change, via drag or programmatically. |
Returns
| Field | Type | Description |
|---|---|---|
| size | number | Current size in pixels. |
| isCollapsed | boolean | Whether the region is currently collapsed. |
| collapse | () => void | Programmatically collapse the region. |
| expand | () => void | Expand from collapsed state. |
| resize | (size: number) => void | Resize to a specific pixel value. |
| props | ResizableProps | Props to spread on the resizable component or pass to ResizeHandle. |
Examples
Common configurations, variations, and states.A collapsible sidebar with snap points, driven by useResizable. Dragging snaps to preset widths, dragging past the minimum collapses the panel, and the expand method restores it programmatically.
Use with shadcn
Already using the shadcn registry workflow? Install the real Astryx package and a local public re-export. Component implementation source stays in Astryx. How compatibility works.
This install URL expires with the draft preview.Install the editable showcase compositionbashnpx shadcn@latest add https://astryx-pxujcw3w9-fbopensource.vercel.app/r/hooks/use-resizable.json
bashnpx shadcn@latest add https://astryx-pxujcw3w9-fbopensource.vercel.app/r/showcases/resizable/default.json