re-resizable: a low-level resize handle for React that stays out of your way
📏 A resizable component for React.
At a glance
- What is it?
- MIT-licensed TypeScript giving you drag handles, bounds, grid snapping and aspect-ratio locking, with nothing imposed about how you store the size. The trade is that you own the state, and the library will not stop you from fighting it.
- Who is it for?
- re-resizable fits layouts where one element's size drives others, such as a sidebar that resizes the pane beside it or a video frame with a fixed ratio. It is the wrong level of abstraction if you want a working split pane, because assembling panels, keyboard resizing and persistence is left to you.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 150 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The one decision that shapes everything else
There is a single fork in the road in this API and it explains most of how the component behaves. Either you give it an initial size and let it own the resizing, or you give it a size on every render and keep the state yourself.
The uncontrolled form is two props:
+import { Resizable } from 're-resizable';
+
+<Resizable
+ defaultSize={{
+ width: 320,
+ height: 200,
+ }}
+>
+ Sample with default size
+</Resizable>
++With `defaultSize` you are done. The `Size` type makes `width` and `height` both optional, so you can set only the width and the height becomes `auto`, which means the element takes 100% of its parent's height. Values can be a number, a `px` string or a percentage.
The controlled form is where the README issues a warning in one line: if you use the `size` prop, please manage state by yourself. The callback receives the size delta, and the example adds that delta to the current state rather than replacing it:
+import { Resizable } from 're-resizable';
+
+<Resizable
+ size={{ width: this.state.width, height: this.state.height }}
+ onResizeStop={(e, direction, ref, d) => {
+ this.setState({
+ width: this.state.width + d.width,
+ height: this.state.height + d.height,
+ });
+ }}
+>
+ Sample with size
+</Resizable>
+Note that `defaultSize` is ignored whenever `size` is set, and that if you omit both, the component starts at `auto`. There is also a small imperative escape hatch: `updateSize(size)` on the instance API, which lets you resize the element without a drag if you are holding a ref.
Snapping, ratios and the props that actually get used
Most people need four or five of these props, so it is worth sorting them by how often they turn up.
`minWidth` and `minHeight` both default to 10px, and `maxWidth` and `maxHeight` accept the same value types. The units are more flexible than the names suggest: the README gives `300`, `50%`, `50vw` and `50vh` as examples, so a limit can be relative to the viewport rather than the element.
Snapping comes in two forms and they behave differently. `grid` takes a `[x, y]` pair of increments and defaults to `[1, 1]`, which effectively means no snapping. `gridGap` defaults to `[0, 0]` and exists so that a layout with visual gaps between cells still resizes to whole cells, with the gap amount multiplied by the number of cells spanned minus one. `snap` instead takes absolute pixel values per axis, so you can define the exact widths a pane is allowed to occupy, and `snapGap` sets how far the pointer must travel before it moves to the next snap target, defaulting to `0`, meaning targets are always used.
`resizeRatio` scales the element by a factor relative to pointer movement, defaulting to `1` for one to one. Setting `2` gives a 2:1 ratio, and the two-element form gives independent `resizeRatioX` and `resizeRatioY`, which arrived in the 6.9.16 release.
For images and video, `lockAspectRatio` takes either `true` to lock to the initial ratio or a number such as `16/9` to lock to a specific one. The README's caveat matters: if you set a numeric ratio, make sure the initial height and width already have that ratio, or the first drag will jump. `lockAspectRatioExtraWidth` and `lockAspectRatioExtraHeight` handle the common case of a 16:9 video beside a fixed sidebar or under a fixed header.
Bounds, handles and enabling specific edges
`bounds` is the prop that prevents the most common failure, and it defaults to nothing. It accepts `'window'`, `'parent'` or an HTMLElement, and setting it is how you stop a pane from being dragged past the edge of its container and out of reach.
`boundsByDirection` exists because of a specific layout problem. By default, maximum dimensions are worked out from the element's left and top position, on the assumption that width grows to the right and height grows downward. If your resizable element is stuck to the right side of the screen and you want the user to resize by dragging the left handle, that assumption is wrong, and `boundsByDirection` switches the calculation to follow the direction the pointer is actually dragging.
Handles are styled two ways. `handleStyles` overrides the style of one or more handles and only the axis you specify is applied, and `handleClasses` does the same for class names. Which handles render is the `enable` prop, taking any combination of `top`, `right`, `bottom`, `left`, `topRight`, `topLeft`, `bottomRight` and `bottomLeft`. Leaving it off gives you all of them, which is usually not what a sidebar wants.
Callbacks are `onResizeStart`, `onResize` and `onResizeStop`. The middle one fires continuously during the drag, and 6.9.16 fixed a case where it fired before snapping to the grid applied, which is the kind of ordering bug that produces a visible jump if you are wiring that callback to something layout-sensitive.
What the tooling in the repository tells you
The tree is a good indicator of how seriously the component is maintained. There is a `.storybook/` directory and a `stories/` folder, and the project publishes a live Storybook at bokuweb.github.io/re-resizable as its demo, alongside three CodeSandbox examples: a plain JavaScript one, a TypeScript one, and one using hooks.
Testing moved to Playwright. `package.json` defines `test` as `npm run test-ct`, which runs `playwright test -c playwright-ct.config.ts`, and there are `playwright/` and `playwright-ct.config.ts` at the root. The 6.9.18 release lists Playwright as an addition, which suggests the browser-based test setup replaced an older one rather than supplementing it.
The build is Rollup plus `tsc`, with a separate ES5 production bundle: `build:prod:main` and `build:prod:es5` run through `scripts/prod.js` and `scripts/prod.es5.js`, and `package.json` points `main` at `./lib/index.es5.js` and `module` at `./lib/index.js`. Running the two builds in serial before `tsc` is what makes the published package work for bundlers and for older toolchains in the same install.
There is a mismatch worth noting. The latest tagged release is 6.10.0 from 2024-09-21, while `package.json` in the tree declares version 6.11.2, so the repository is ahead of the newest release tag. The last push was on 2026-05-12 and the project is not archived. Renovate handles dependency updates and Prettier formats the TypeScript, and 6.9.18 records the switch to pnpm, which is why `pnpm-lock.yaml` is the lockfile in the tree.
Where it sits against the alternatives
The difference between re-resizable and a panel library is the level at which each one stops helping you. re-resizable draws handles and reports deltas. It does not lay out sibling panes, keep them in proportion, or persist anything.
react-rnd is the closest neighbour and comes at this from the other direction: it is a draggable and resizable element rather than a resize-only one, so if you also need free movement, it covers both. The trade is that it carries more machinery than you need when position is fixed.
If the requirement is a working split view rather than a resizable box, react-resizable-panels or the split-pane component inside MUI will get you to a usable layout in less code, at the cost of adopting their layout model. MUI's own resizable panel is a reasonable pick if you are already inside a MUI application.
The case for re-resizable specifically is when one element's size has to drive something structural, and you need to know the exact numbers. A video editor with a fixed-aspect preview beside a settings column, a terminal-like panel inside a grid, a resizable column in a table: those cases need the raw width and height, and they need the boundary rules re-resizable exposes through `bounds`, `gridGap` and `snapGap`. Those are also the cases where the controlled-mode requirement stops being a cost, because you were going to hold that state anyway.
Editorial conclusion
re-resizable fits layouts where one element's size drives others, such as a sidebar that resizes the pane beside it or a video frame with a fixed ratio. It is the wrong level of abstraction if you want a working split pane, because assembling panels, keyboard resizing and persistence is left to you. Start with the uncontrolled `defaultSize` form to see the handles behave, then move to the controlled `size` prop once you know which boundaries you need, and set `bounds` explicitly rather than accepting the default, since a component that grows past its container is the most common way this goes wrong.
Frequently asked questions
What is re-resizable in React?
A MIT-licensed TypeScript component that adds drag handles to a box and reports its new size. It handles bounds, minimum and maximum dimensions, grid snapping, aspect ratio locking and configurable handle styling, and it installs from npm as `re-resizable`.
How do I use defaultSize versus size in re-resizable?
Use `defaultSize` for an initial size and let the component manage resizing itself. Use `size` when you hold the dimensions in your own state, in which case `onResizeStop` gives you a delta to apply. The README notes that `defaultSize` is ignored whenever `size` is set, and that omitting both starts the component at `auto`.
How do I stop a re-resizable pane from overflowing its container?
Set the `bounds` prop, which accepts `window`, `parent` or a specific HTMLElement. If the element sits against a screen edge and you want to resize by dragging the opposite handle, add `boundsByDirection`, since maximum dimensions otherwise work from the element's left and top position.
Can re-resizable snap to a grid with gaps?
Yes. `grid` sets the increments to snap to and defaults to `[1, 1]`, while `gridGap` defaults to `[0, 0]` and accounts for visual gaps between grid cells, multiplying the gap by the number of cells spanned minus one. If you need specific pixel positions instead, `snap` takes absolute values per axis and `snapGap` sets how far the pointer must travel to reach the next one.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bokuweb-re-resizable)