How the Astryx Resizable Component Handles Panel Resizing
The Astryx Resizable component uses the useResizable hook to manage panel resizing through a unified API that handles size clamping, snap points, collapse states, and localStorage persistence for both single and multi-region layouts.
The Astryx design system provides a robust solution for building draggable, collapsible panel interfaces in React applications. According to the facebook/astryx source code, the Resizable component delegates all interaction logic to a specialized hook that supports percentage-based defaults, collapsible regions, and automatic state persistence. This guide examines the internal architecture and implementation patterns that enable smooth panel resizing with snapping and persistence capabilities.
Core Architecture of the Resizable Hook
The resizing system centers on useResizable, a public hook exposed in packages/core/src/Resizable/useResizable.ts that provides two distinct overloads: one for single-region configurations (UseResizableSingleConfig) and one for multi-region layouts (UseResizableMultiConfig). The hook automatically delegates to internal implementations based on the configuration object provided.
Single-Region Resizing with useSingleResizable
For individual panels, the hook utilizes useSingleResizable to manage the complete lifecycle of a resizable region. This internal handler initializes the panel size from default values, persisted localStorage data, or percentage-based calculations, then enforces constraints through minSizePx and maxSizePx boundaries.
The hook tracks collapse state and supplies callback references for the resize-handle lifecycle: _onResizeStart, _onResizeMove, and _onResizeEnd. These callbacks handle drag initiation, delta calculations, and final size commitments, though onResizeEnd is typically a no-op since changes apply during the move phase.
Multi-Region Layout Management
When configuring multiple resizable areas, useMultiResizable iterates over a stable map of region configurations, invoking useSingleResizable for each key. This approach requires a static regions map to guarantee stable hook order across renders, returning a lookup object where each region maintains its own independent state and callback set.
Persistence and State Restoration
The persistence layer utilizes loadPersistedSize and persistSize helpers to store panel dimensions in localStorage under the prefix astryx-resizable:. When an autoSaveId is supplied in the configuration, the hook automatically retrieves previous dimensions on mount and writes updates after each resize operation, enabling panels to restore their exact width after page reloads.
Resize Handle Props and Callback Binding
The hook packages current size, collapse flags, and internal callbacks into a ResizableProps object. These props are deliberately prefixed with _ (such as _size, _minSizePx, _onResizeStart) to maintain a clean public API surface while providing the necessary data for Astryx's built-in ResizeHandle component or custom implementations.
Implementation Details and Edge Cases
Default Size Resolution and Percentage Handling
The resolveDefaultSize utility accepts numeric values, pixel strings (e.g., "260px"), or percentage strings (e.g., "20%"). For percentage values, the system calculates approximate pixel equivalents using window.innerWidth (or a fallback width during SSR), ensuring responsive initial sizing across different viewports.
Clamping and Snap Point Logic
The clampSize function enforces boundary constraints in two phases. First, it restricts the raw value to absolute min and max limits. Then, if snaps are defined, it selects the nearest snap point from the array, guaranteeing the panel never rests between discrete values. This creates a magnetic effect where panels automatically settle into predefined widths.
Collapse and Expand Mechanics
For collapsible panels, the system maintains a preCollapseSizeRef that stores the current size before shrinking to zero. When the collapse function executes, it preserves the previous dimension and sets the size to 0. The expand function restores this saved value (or falls back to the default) and re-applies clamping logic to ensure the panel returns to valid dimensions.
The collapse threshold activates during drag operations: if the drag delta moves the panel below collapsedSize, the hook automatically toggles the isCollapsed flag. Conversely, dragging above the threshold re-expands the panel and restores the saved dimension.
Drag Lifecycle and Delta Processing
During onResizeStart, the hook records the initial size to establish a baseline. onResizeMove processes the drag delta, checks against collapse thresholds, updates the internal size state, and fires external callbacks. Because the system applies size changes in real-time during the move phase, onResizeEnd requires no additional logic beyond optional cleanup.
Practical Implementation Examples
Basic Single-Panel Resizing
The following example demonstrates a vertical side navigation panel with snapping, collapsing, and persistence:
import {useResizable} from '@astryxdesign/core';
function SideNav() {
const {props, collapse, expand} = useResizable({
defaultWidth: 260,
minWidth: 180,
maxWidth: 480,
collapsible: true,
collapsedSize: 40,
snaps: [180, 260, 360, 480],
autoSaveId: 'side-nav-width',
onWidthChange: (w) => console.log('Width changed to', w),
});
return (
<div
style={{width: props._size, minWidth: props._minSizePx}}
{...props}
>
{/* panel contents */}
<button onClick={collapse}>Collapse</button>
<button onClick={expand}>Expand</button>
</div>
);
}
The props object spreads _onResizeStart, _onResizeMove, and other internal callbacks onto the container, enabling interaction with Astryx's ResizeHandle component.
Multi-Region Split View Layout
For complex layouts with multiple resizable areas, use the multi-region configuration:
import {useResizable} from '@astryxdesign/core';
function SplitView() {
const regions = useResizable({
direction: 'horizontal',
regions: {
left: {defaultSize: '30%', minSizePx: 150, collapsible: true},
right: {defaultSize: '70%', minSizePx: 300},
},
autoSaveId: 'splitview',
});
return (
<div style={{display: 'flex'}}>
<div {...regions.left.props}>Left pane</div>
<div {...regions.right.props}>Right pane</div>
</div>
);
}
Each region maintains independent state while sharing the autoSaveId namespace for coordinated persistence.
Pre-Built Resizable Sidebar Template
The Astryx CLI provides a ready-made component that internally wires the hook:
import ResizableSidebar from '@astryxdesign/cli/assets/templates/blocks/components/Resizable/ResizableSidebar';
export default function App() {
return <ResizableSidebar autoSaveId="app-sidebar" defaultWidth={260} collapsible />;
}
This template, located at packages/cli/assets/templates/blocks/components/Resizable/ResizableSidebar.tsx, demonstrates production-ready implementation of the resizable patterns.
Key Source Files
The panel resizing logic spans several critical files in the facebook/astryx repository:
-
packages/core/src/Resizable/useResizable.ts: Core hook implementing single- and multi-region resizing, snapping, collapsing, and persistence. -
packages/cli/assets/templates/blocks/components/Resizable/ResizableSidebar.tsx: Example component demonstrating production-ready consumption of the hook. -
apps/storybook/stories/Resizable.stories.tsx: Interactive documentation and visual verification of resizing behaviors.
Summary
-
Unified Hook Architecture: The
useResizablehook inpackages/core/src/Resizable/useResizable.tsprovides a single entry point for both single and multi-region panel resizing, delegating touseSingleResizableoruseMultiResizablebased on configuration. -
Automatic Persistence: Panel dimensions automatically save to
localStorageunder theastryx-resizable:prefix whenautoSaveIdis specified, restoring previous sizes across sessions. -
Constraint Enforcement: The
clampSizefunction enforcesminSizePx/maxSizePxboundaries and snaps to predefined points, ensuring panels always rest at valid dimensions. -
Collapse State Management: Collapsible panels store pre-collapse dimensions in
preCollapseSizeRef, enabling full restoration when expanding from a collapsed state. -
Real-time Updates: Size changes apply during
onResizeMovecallbacks, providing immediate visual feedback without waiting for drag completion.
Frequently Asked Questions
How does the Astryx Resizable component persist panel sizes across page reloads?
The hook checks for an autoSaveId configuration property and uses loadPersistedSize to retrieve previous dimensions from localStorage on initialization. After each resize operation, persistSize writes the current dimension using the key prefix astryx-resizable:. This occurs automatically without requiring manual intervention from the consuming component.
What is the difference between single-region and multi-region resizing in Astryx?
Single-region mode uses useSingleResizable to manage one independent panel with direct access to collapse, expand, and props methods. Multi-region mode utilizes useMultiResizable to handle multiple panels simultaneously by mapping over a static regions configuration object, returning a dictionary of region-specific hook results while maintaining stable React hook ordering.
How does snapping work when resizing panels near defined snap points?
The clampSize function first applies absolute min/max constraints, then calculates the nearest value from the snaps array if provided. When the user releases the drag handle, the panel automatically animates or snaps to the closest predefined width, preventing the panel from resting at intermediate sizes between snap points.
Can panels use percentage-based default sizes instead of fixed pixels?
Yes, the resolveDefaultSize utility supports percentage strings (e.g., "30%") alongside numeric pixel values. During initialization, the system converts percentages to approximate pixel values using window.innerWidth (or a fallback for SSR), allowing responsive default sizing that adapts to the viewport while maintaining pixel precision during drag operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →