# How the Astryx Resizable Component Handles Panel Resizing

> Discover how the Astryx Resizable component manages panel resizing with its useResizable hook, offering size clamping, snap points, collapse states, and localStorage persistence.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: internals
- Published: 2026-08-06

---

**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`](https://github.com/facebook/astryx/blob/main/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:

```tsx
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:

```tsx
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:

```tsx
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`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/packages/cli/assets/templates/blocks/components/Resizable/ResizableSidebar.tsx)**: Example component demonstrating production-ready consumption of the hook.

- **[`apps/storybook/stories/Resizable.stories.tsx`](https://github.com/facebook/astryx/blob/main/apps/storybook/stories/Resizable.stories.tsx)**: Interactive documentation and visual verification of resizing behaviors.

## Summary

- **Unified Hook Architecture**: The `useResizable` hook in [`packages/core/src/Resizable/useResizable.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/Resizable/useResizable.ts) provides a single entry point for both single and multi-region panel resizing, delegating to `useSingleResizable` or `useMultiResizable` based on configuration.

- **Automatic Persistence**: Panel dimensions automatically save to `localStorage` under the `astryx-resizable:` prefix when `autoSaveId` is specified, restoring previous sizes across sessions.

- **Constraint Enforcement**: The `clampSize` function enforces `minSizePx`/`maxSizePx` boundaries 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 `onResizeMove` callbacks, 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.