Fluxo UIFluxo UIv0.4.93

Resizable

A generic resize wrapper. Drop it around any React node to gain 8-direction resizing with min/max, aspect-ratio lock, grid snap, keyboard support, and bounds-awareness — all themeable and dark-mode ready.

Basic Usage

Eight handles by default. Drag to resize, or focus a handle with Tab and use arrow keys (Shift for bigger steps).

Basic Resize

Grab any of the 8 handles (4 corners, 4 edges) to resize. Keyboard arrows work too — focus a handle with Tab.

Drag any edge or corner
Current size: 320 × 200 px
import { Resizable } from 'fluxo-ui';

<Resizable defaultWidth={320} defaultHeight={200} minWidth={120} minHeight={80}>
  <div className="p-4">
    Resize me using any of the 8 handles.
  </div>
</Resizable>

Axis & Handle Sets

Lock resizing to a single axis or render only a subset of handles.

Axis & Handle Sets

Restrict resize to one axis or render only a subset of handles.

axis: horizontal
axis: vertical
handles: corners
handles: edges
<Resizable axis="horizontal" defaultWidth={280} defaultHeight={140}>...</Resizable>
<Resizable axis="vertical" defaultWidth={240} defaultHeight={160}>...</Resizable>
<Resizable handles="corners" defaultWidth={240} defaultHeight={160}>...</Resizable>

Aspect Ratio & Grid Snap

Pass aspectRatio={16 / 9} for a fixed ratio, or grid={[20, 20]} to snap to a pixel grid.

Aspect Ratio & Grid Snap

Lock to an aspect ratio while resizing, or snap to a pixel grid.

16:9 aspect
1:1 square
grid: 20×20
<Resizable aspectRatio={16 / 9} defaultWidth={320} defaultHeight={180}>...</Resizable>
<Resizable grid={[20, 20]} defaultWidth={320} defaultHeight={200}>...</Resizable>

Resizable Media

Wrap an <img>, iframe, or anything else. Use showHandles="hover" for a cleaner UX.

Resizable Media

Wrap any element — including images, iframes, or charts. Handles only appear on hover with showHandles='hover'.

Hover to reveal handles
<Resizable
  defaultWidth={300}
  defaultHeight={200}
  minWidth={120}
  minHeight={80}
  showHandles="hover"
  ariaLabel="Photo"
>
  <img src="/photo.jpg" alt="Mountain view" style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: 8 }} />
</Resizable>

Controlled Mode

Pass width + height + onSizeChange for full two-way binding.

Controlled Mode

Provide width and height props (and listen to onSizeChange) for two-way binding with external controls.

Drive me from the sliders below
const [size, setSize] = useState({ width: 320, height: 200 });

<Resizable
  width={size.width}
  height={size.height}
  onSizeChange={setSize}
  minWidth={120}
  minHeight={80}
>
  ...
</Resizable>
<input type="range" min={120} max={500} value={size.width}
  onChange={(e) => setSize(s => ({ ...s, width: +e.target.value }))} />

Import

import { Resizable } from 'fluxo-ui';
import type { ResizableProps, ResizableSize } from 'fluxo-ui';

Resizable Props

childrenreq
React.ReactNode

Element(s) wrapped by the resizable container.

defaultWidth
number | string

Initial width in pixels (uncontrolled). Strings are parsed for the leading number.

defaultHeight
number | string

Initial height in pixels (uncontrolled).

width
number

Controlled width in pixels. Provide together with `height` and listen to `onSizeChange`.

height
number

Controlled height in pixels. Provide together with `width` and listen to `onSizeChange`.

onSizeChange
(size: { width: number; height: number }) => void

Fires on every committed size change (mouse, touch, keyboard).

onResizeStart
(event: ResizeStartEvent) => void

Fires when the user grabs a handle.

onResize
(event: ResizeChangeEvent) => void

Fires continuously while resizing.

onResizeEnd
(event: ResizeEndEvent) => void

Fires when the user releases the handle.

axis
'horizontal' | 'vertical' | 'both'"'both'"

Restrict resize direction. Handles incompatible with the axis are hidden.

handles
ResizeHandlePosition[] | 'all' | 'corners' | 'edges'"'all'"

Which handles to render. `'corners'` = `['ne','nw','se','sw']`, `'edges'` = `['n','s','e','w']`.

minWidth
number"40"

Minimum width in pixels.

minHeight
number"40"

Minimum height in pixels.

maxWidth
number"Infinity"

Maximum width in pixels.

maxHeight
number"Infinity"

Maximum height in pixels.

grid
[number, number]

Snap size to a [widthStep, heightStep] grid.

aspectRatio
number | boolean

Lock to a specific aspect ratio (number) or to the starting ratio (true).

lockAspectRatio
boolean"false"

Convenience alias for `aspectRatio = true`.

disabled
boolean"false"

Disable all resize interaction; handles are hidden.

handleSize
number"8"

Visual / hit area thickness of each handle in pixels.

showHandles
'always' | 'hover' | 'never'"'always'"

When handles are visible. `'hover'` reveals them on container hover or focus.

keyboardStep
number"8"

Pixels per arrow-key press on a focused handle.

keyboardBigStep
number"32"

Pixels per Shift+arrow press on a focused handle.

bounds
'parent' | 'window' | { width: number; height: number }

Optional upper bound for size. Useful inside scroll containers.

className
string

Class name applied to the container.

style
React.CSSProperties

Inline styles for the container (merged with computed width/height).

handleClassName
string

Class name applied to every resize handle.

ariaLabel
string

Used as the prefix for each handle's aria-label, e.g. "Widget resize se".

Features

Wraps Any Element

Drop the Resizable wrapper around any React element — images, iframes, charts, divs.

8 Handles or Subset

All edges and corners by default. Pick "edges", "corners", or your own list.

Aspect Ratio Lock

Number for a fixed ratio (16:9, 1:1) or true to keep the starting ratio.

Grid Snap

Provide [stepX, stepY] for perfectly-aligned resize values.

Touch + Keyboard

Mouse, touch, and arrow keys all supported. Shift + arrow for big steps.

Bounds Aware

Constrain to parent, window, or a custom { width, height } bound.

Controlled or Uncontrolled

Use defaultWidth/defaultHeight for autonomous control, or width/height for two-way binding.

Themed Handles

Handles use --eui-* tokens — flip automatically across brand themes and dark mode.