EXEPERTAI LAB

Research alpha

Time Machine
EXEPERTAI LAB
GALLERY / COLLECTION
← Browse Astryx gallery
Resizable·component·@astryxdesign/core

Resizable

Hook-based resizable panel system. useResizable() manages size state and ResizeHandle provides the interactive pill-grip separator. Pass resize props to existing layout components via their resizable prop.

Open in Playground @astryxdesign/core/Resizable

Showcases and examples

1 documented example

Resizable

Horizontal resizable split with a draggable handle between two panels.

Preview loads on approachPreview loads on approach
Exact source · resizable-showcase
// Copyright (c) Meta Platforms, Inc. and affiliates.

'use client';

import {useResizable, ResizeHandle} from '@astryxdesign/core/Resizable';
import {
  Card,
  Layout,
  LayoutContent,
  LayoutPanel,
  VStack,
} from '@astryxdesign/core/Layout';
import {Text, Heading} from '@astryxdesign/core/Text';

export default function ResizableShowcase() {
  const sidebar = useResizable({
    defaultSize: 200,
    minSize: 120,
    maxSize: 400,
  });

  return (
    <Card variant="muted" height={280} width={600}>
      <Layout
        height="fill"
        start={
          <>
            <LayoutPanel width={sidebar.size} hasDivider={false}>
              <VStack gap={2}>
                <Heading level={4}>Sidebar</Heading>
                <Text color="secondary">{Math.round(sidebar.size)}px wide</Text>
              </VStack>
            </LayoutPanel>
            <ResizeHandle
              direction="horizontal"
              hasDivider
              resizable={sidebar.props}
              label="Resize sidebar"
            />
          </>
        }
        content={
          <LayoutContent>
            <VStack gap={2}>
              <Heading level={4}>Content</Heading>
              <Text color="secondary">
                Drag the handle to resize the sidebar.
              </Text>
            </VStack>
          </LayoutContent>
        }
      />
    </Card>
  );
}

Usage

Hook-based resizable panel system. useResizable() manages size state and ResizeHandle provides the interactive pill-grip separator. Pass resize props to existing layout components via their resizable prop.

  • Use percent(value, {min: pixel(value)}) or percent(value, {max: pixel(value)}) when a percentage needs exactly one pixel floor or ceiling. Reuse Table’s pixel() helper; numbers, exact Npx, and pixel(value) remain pixels. State, persistence, callbacks, paint, and ARIA remain resolved pixels.
  • Use useResizable() with existing Astryx layout components. Pass the returned props to the resizable prop on LayoutPanel or SideNav.
  • Provide an accessible label on each ResizeHandle when multiple handles exist (e.g. "Resize sidebar", "Resize terminal").
  • Wrap panels in extra container components for resize. The hook-first architecture avoids extra DOM; use it directly on existing components.

Typed props

useResizable

PropType and behavior
defaultSizeSizeValue | ResizableSize · 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 exactly one and resolves once.
minSizeResizableSize · default 50
Live minimum. 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.
maxSizeResizableSize · default Infinity
Live maximum. 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.
collapsibleboolean · default false
Whether the region can collapse to size 0.
collapsedSizenumber · default 40
Pixel threshold that triggers collapse during drag.
snapsnumber[]
Pixel values to snap to during resize.
shrinkOrdernumber
Cascade priority: lower number shrinks first.
autoSaveIdstring
Key for persisting sizes and collapse state to localStorage.

ResizeHandle

PropType and behavior
direction'horizontal' | 'vertical' · default 'horizontal'
Layout direction: determines cursor and indicator orientation.
isReversedboolean · default false
Reverse drag direction. Use when the handle controls a panel on the end/right/bottom side.
isDisabledboolean · default false
Whether the handle is interactive.
hasDividerboolean · default false
Show a full-length 1px divider line through the handle. Use when adjacent panels share the same background.
isAlwaysVisibleboolean · default true
Show the pill grip at rest instead of only on hover. Use when discoverability is important.
pillPlacement'start' | 'end' | 'center' | 'auto' · default 'auto'
Which side of the divider the pill sits on. auto = content side (derived from isReversed), flips when collapsed. start = left/top, end = right/bottom, center = centered on divider.
labelstring · default 'Resize handle'
Accessible label for the separator.
resizableResizableProps · required
Resize props from useResizable: connects handle to panel. Carries the region's axis ('horizontal' | 'vertical'), which must match this handle's direction.
childrenReactNode
Custom handle content. Overrides the default pill + divider.
xstyleStyleXStyles
StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value, not an inline style object like style={{}}.

Anatomy

Handle · required

Focusable separator that owns pointer and keyboard resize interaction.

Grip pill · optional

Default visible grip indicator; custom handle content can replace it.

Grab zone · required

Invisible enlarged pointer region aligned with the grip or divider.

Theming

Targets

astryx-resize-handle

Visual props: direction

astryx-resize-handle-pill