mrkeyoor.com_
Sat 08 Aug 20:58 UTC
npmWeb Frontendupdated 08 Aug 2026

react-resizable

react-resizable adds mouse and touch drag handles to a React element so a user can change its pixel width and height. It offers a low-level controlled Resizable component and a simpler ResizableBox that keeps size in local state. Constraints, aspect-ratio locking, eight handle positions, drag-grid snapping, and scaled-parent correction cover the common panel and dashboard cases. It is a focused interaction component, not a layout system, splitter, ResizeObserver wrapper, or accessibility-complete widget.

Verdict

A good focused choice for mouse and touch resizing when your React app already owns layout state. Skip it for accessible splitters, responsive track sizing, or full dashboard layout management unless you are prepared to build those layers yourself.

API stability4/5The changelog says the 4.0.0 runtime API and on-screen behavior stayed unchanged while the source and declarations moved from Flow to TypeScript. Existing Resizable and ResizableBox concepts have lasted across majors, but Flow support was removed, the bundled types replaced DefinitelyTyped, and the version 3 custom-handle contract made forwarding a DOM ref mandatory.
Docs4/5The README documents both components, every public prop, the mandatory stylesheet, TypeScript migration, compatibility by major, custom-handle ref forwarding, and a live example page that currently returns 200. It is unusually clear about style width and height being ignored by ResizableBox, but accessibility expectations and production testing guidance are largely absent.
Maintenance5/5Version 4.0.2 was published on June 18, 2026, the repository was pushed on August 5, 2026, and the changelog records recent fixes for declaration optionality, aspect-ratio cursor tracking, resize drift, stale stop values, and server rendering. GitHub reports 12 open issues and pull requests, which is a manageable tracker for a focused package rather than evidence of abandonment.
Ecosystem4/5npm recorded 4,165,774 downloads for the measured week, and the peer range supports React and React DOM from 16.3 onward. Its react-draggable foundation is well established, bundled declarations remove a separate type package, and the component fits ordinary React styling. The surrounding ecosystem is still narrower than full layout systems because persistence, accessibility, and sibling coordination are intentionally outside its scope.

Use it if

  • You need draggable resize handles on a React panel and want both a quick stateful component and a controlled building block
  • You need minimum and maximum dimensions, fixed aspect ratios, grid snapping, or handles on specific edges and corners
  • Your UI already owns the resulting width and height and can apply those pixel values to its layout
  • You need a small focused primitive that works across React versions from 16.3 onward
Skip it if

Setup reality

Installation is npm install react-resizable, but a working import is not the whole setup. React and React DOM 16.3 or newer are peer dependencies, while react-draggable and prop-types arrive as runtime dependencies. You must also import react-resizable/css/styles.css or copy that stylesheet into a bundler that cannot import CSS; without it, the default handles are invisible and do not work properly. Version 4 ships its own TypeScript declarations, so remove the old @types/react-resizable package or TypeScript may resolve competing declarations. ResizableBox is quickest, but its style width and height are deliberately ignored because internal state owns those values. Resizable is controlled: every onResize callback must update the width and height props and the child style, or the handle moves without a lasting size change. Custom handle components must forward the provided ref and remaining pointer props to a real DOM element, otherwise react-draggable cannot attach correctly and can report that DraggableCore was not mounted. Sizes and constraints are numeric pixels. A transformed parent needs the matching transformScale value, and grid snapping goes through draggableOpts. Finally, the default drag handle is pointer-oriented; keyboard behavior, ARIA labeling, persistence, responsive conversion, and neighboring layout updates remain application work.

Patterns

Render a stateful resizable boxbasic-resizable-box

import { ResizableBox } from 'react-resizable';
import 'react-resizable/css/styles.css';

export function Panel() {
  return (
    <ResizableBox width={320} height={180}>
      <div>Drag the southeast handle</div>
    </ResizableBox>
  );
}

The CSS import is required for the default handle to be visible and positioned correctly.

Keep size in parent statecontrolled-resizable

import { useState } from 'react';
import { Resizable } from 'react-resizable';

export function ControlledPanel() {
  const [size, setSize] = useState({ width: 320, height: 180 });
  return (
    <Resizable
      width={size.width}
      height={size.height}
      onResize={(_, data) => setSize(data.size)}
    >
      <div style={{ width: size.width, height: size.height }}>Content</div>
    </Resizable>
  );
}

Resizable has no size state; update both its props and the child dimensions from each callback.

Set minimum and maximum dimensionsconstrain-size

<ResizableBox
  width={300}
  height={200}
  minConstraints={[180, 120]}
  maxConstraints={[720, 480]}
>
  <div>Constrained content</div>
</ResizableBox>

Constraint tuples are [width, height] in pixels; defaults are [20, 20] and [Infinity, Infinity].

Allow horizontal resizing onlyresize-one-axis

<ResizableBox
  width={280}
  height={160}
  axis="x"
  resizeHandles={['e']}
>
  <div>Width only</div>
</ResizableBox>

Match the handle to the allowed axis; a north or south handle cannot produce a horizontal delta.

Preserve the starting aspect ratiolock-aspect-ratio

<ResizableBox
  width={320}
  height={180}
  lockAspectRatio
  minConstraints={[160, 90]}
  maxConstraints={[960, 540]}
>
  <div>16:9 preview</div>
</ResizableBox>

The ratio comes from the current width and height, so start with the exact proportion you want to preserve.

Enable every edge and cornermultiple-handles

<ResizableBox
  width={360}
  height={240}
  resizeHandles={['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw']}
>
  <div>Resize from any side</div>
</ResizableBox>

The supplied CSS defines directional classes; custom CSS must position every handle class you enable.

Render a custom handle with its required refcustom-resize-handle

import { forwardRef } from 'react';

const Handle = forwardRef(function Handle({ handleAxis, ...props }, ref) {
  return (
    <button
      ref={ref}
      type="button"
      aria-label={`Resize from ${handleAxis}`}
      className={`resize-handle resize-handle-${handleAxis}`}
      {...props}
    />
  );
});

<ResizableBox width={300} height={180} handle={<Handle />}>
  <div>Custom handle</div>
</ResizableBox>

Forward the received ref and pointer props to the DOM node; an ARIA label alone does not add keyboard resizing.

Snap drag deltas to a gridsnap-to-grid

<ResizableBox
  width={300}
  height={200}
  draggableOpts={{ grid: [20, 20] }}
>
  <div>20 px increments</div>
</ResizableBox>

draggableOpts is passed to react-draggable's DraggableCore; grid values control movement deltas, not CSS layout tracks.

Correct dragging inside a scaled parentcorrect-transform-scale

<div style={{ transform: 'scale(0.75)', transformOrigin: 'top left' }}>
  <ResizableBox width={400} height={240} transformScale={0.75}>
    <div>Scaled editor canvas</div>
  </ResizableBox>
</div>

Set transformScale to the parent's actual scale or pointer movement and size deltas will not line up.

Handle resize start, progress, and stoptrack-resize-lifecycle

<ResizableBox
  width={320}
  height={200}
  onResizeStart={() => setDragging(true)}
  onResize={(_, { size, handle }) => preview(size, handle)}
  onResizeStop={(_, { size }) => {
    setDragging(false);
    saveSize(size);
  }}
>
  <div>Persistent panel</div>
</ResizableBox>

Persist on stop rather than on every drag event; the callback data also identifies the handle being used.

Type a resize callback in TypeScripttype-resize-callback

import type { ResizeCallbackData } from 'react-resizable';
import type { SyntheticEvent } from 'react';

function onResize(
  event: SyntheticEvent,
  { node, size, handle }: ResizeCallbackData,
+) {
  console.log(node, size.width, size.height, handle);
}

Version 4 exports declarations from the package root; uninstall @types/react-resizable if it remains in the project.

Style the stateful wrapperstyle-resizable-box

<ResizableBox
  width={320}
  height={200}
  className="settings-panel"
  style={{ border: '1px solid #bbb', overflow: 'auto' }}
>
  <div>Scrollable settings</div>
</ResizableBox>

Do not put width or height in style; ResizableBox ignores those two style properties and uses its internal size.

Alternatives

PackageRegistryPick it when
re-resizablenpmUse it when you want more built-in size forms and handle styling in a React-specific resizer.
react-rndnpmUse it when the same React element must be both draggable and resizable with bounded movement.
@interactjs/interactjsnpmUse it for framework-neutral dragging, resizing, gestures, snapping, and inertia beyond a single React component.