react-resize-detector: a ResizeObserver hook, and a warning to check CSS first
A Cross-Browser, Event-based, Element Resize Detection for React
At a glance
- What is it?
- react-resize-detector is an MIT-licensed TypeScript hook that turns element size changes into React state, built on the native ResizeObserver, with throttling, debouncing and a callback-only mode. The judgement is that the library's own README tells you to consider CSS Container Queries before reaching for it, which is the right advice and also the sharpest limit on its value.
- Who is it for?
- react-resize-detector fits a React team that needs element dimensions in JavaScript, for a chart that must redraw at a new width or a layout that cannot be expressed as a container query, and that wants the measurement to arrive as component state rather than as a manual listener. It does not fit a layout that CSS can express, because the project says so itself.
- 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?
- Activity is slowing. The repository last received commits 6 months 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 October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The README tells you not to use it, in most cases
The most striking thing about this project is a section titled Should you use this library, and the first line of it is a redirection.
The advice is to consider CSS Container Queries first. They now work in all major browsers, the documentation points at the caniuse entry, and they might solve your use case with pure CSS. The example that follows is a container with `container-type: inline-size` and a `@container (min-width: 700px)` rule that changes a heading's font size, with the default styles above it and the container query below.
That framing is worth respecting rather than skipping. A hook that measures an element and puts the size into React state exists to feed JavaScript logic, and if your logic is really a style change, the browser can do it without a re-render at all.
So the library's own guidance is that you should use it when you need JavaScript-based resize logic with full TypeScript support, complex calculations based on dimensions, integration with React state and effects, or programmatic control over resize behaviour. Those four bullets are the real feature list, and the first section of the README is the accurate summary of when to reach for it.
What the library does provide is thin. It is a hook, `useResizeDetector`, that wraps a native `ResizeObserver` and exposes the observed element's `width` and `height` as React state, with the element ref returned so it can be attached. The README is blunt about the implementation: no `window.resize` listeners, no timeouts. The badge claims about 2kb and a bundle size link, and the project states it is written in TypeScript.
The hook returns a ref, and the state comes from it
The basic usage is short enough to hold in your head.
import { useResizeDetector } from 'react-resize-detector';
const CustomComponent = () => {
const { width, height, ref } = useResizeDetector<HTMLDivElement>();
return <div ref={ref}>{`${width}x${height}`}</div>;
};Three things come back from the hook. The `width` and `height` are the state, and the `ref` is what you attach to the element you want measured. The generic parameter is the element type, so `useResizeDetector<HTMLDivElement>()` gives you a ref typed for a div.
The callback variant adds an `onResize` handler, and the payload tells you whether the element still exists:
import { useCallback } from 'react';
import { useResizeDetector, OnResizeCallback } from 'react-resize-detector';
const onResize: OnResizeCallback = useCallback((payload) => {
if (payload.width !== null && payload.height !== null) {
console.log('Dimensions:', payload.width, payload.height);
} else {
console.log('Element unmounted');
}
}, []);The null check on the dimensions is the detail that matters. When the observed element goes away, the payload carries nulls rather than numbers, and that is the signal you use to distinguish a resize from an unmount.
There is a third form, and the documentation discourages it. With an external ref, you pass `targetRef` to the hook and attach it yourself:
const targetRef = useRef<HTMLDivElement>(null);
const { width, height } = useResizeDetector({ targetRef });The note above that example says it is not advised to use this approach, as dynamically mounting and unmounting the observed element could lead to unexpected behavior. The internal ref avoids that because the hook controls the lifecycle of the observation.
Nine props, and a default refresh rate you should think about
The prop list is short enough to read in one go, and a couple of the entries carry more weight than the others.
`onResize` is the callback, with the default `undefined`. `handleWidth` and `handleHeight` are booleans that trigger updates on width changes and height changes, both defaulting to `true`, so you can watch one axis only.
`skipOnMount` defaults to `false`, and setting it true skips the first resize event when the component mounts. That is worth knowing about, because the initial ResizeObserver callback arrives shortly after mount, and if your handler does something expensive or fires an analytics event, you will get one for the initial size whether you wanted it or not.
`disableRerender` defaults to `false`. Setting it to true means the hook will not trigger re-renders, and only the `onResize` callback will be called. That is the mode to reach for when you need the measurement but do not want the component to re-render on every resize, and it is the main reason the callback variant exists.
`refreshMode` is the rate limiting strategy, either `'throttle'` or `'debounce'`, and the default is `undefined`. `refreshRate` is the delay in milliseconds, with a default of `1000`. That is a one second delay by default, which is a deliberate choice to avoid a re-render storm from a continuous resize, and it is also something to check against your own expectations, because a hook that reports the width a second late behaves very differently from one that reports it on the next frame.
`refreshOptions` takes `{ leading?: boolean; trailing?: boolean }` for the throttle and debounce behaviour, and the documentation points at the es-toolkit docs for the semantics. `observerOptions` is passed straight to `resizeObserver.observe`, so anything the native API accepts is available here. And `targetRef` is the external ref discussed above.
Debouncing is the right default for a chart that redraws
The advanced examples in the documentation are where the design choices become concrete, and the chart resizing example is the one to read.
const Chart = () => {
const chartRef = useRef(null);
const { width, height, ref } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 100,
});
useEffect(() => {
if (width && height && chartRef.current) {
// Redraw chart with new dimensions
}Two things are being changed from the defaults. The mode becomes `debounce`, and the rate drops from 1000 to 100 milliseconds. For a redraw on every measurement, debouncing is the correct choice, because a throttle emits on the leading edge and then periodically during the drag, which means redrawing a chart several times per second while the user is still resizing the container. Debouncing waits for the size to settle.
The `if (width && height && chartRef.current)` guard is the other detail. The hook can report null dimensions when the element unmounts, and the effect has to tolerate that rather than trying to redraw a chart whose container is gone.
The responsive component example uses the width value directly for style decisions, computing padding, font size and flex direction from width thresholds, which is the case where the library is appropriate but where a container query would also work.
The rate limiting is delegated to `es-toolkit`, which is the only runtime dependency in the package manifest, at `^1.45.1`. That is a small and deliberate dependency surface, and it is also why the `refreshMode` documentation points at the es-toolkit site rather than describing the behaviour in the README.
Version 12, a single runtime dependency and a pnpm lockfile
The packaging is small and worth reading in full, because the size claims in the README depend on it.
The package is `react-resize-detector` at version `12.3.0` in the manifest, marked as an ES module with `"type": "module"`, and the exports map points at a single entry, `./build/index.js`, with types at `./build/index.d.ts`. The `files` array contains only `build`, so nothing but the compiled output is published. And `"sideEffects": false` tells a bundler that importing the module has no side effects, which is what lets the tree shaking work.
There is exactly one runtime dependency, `es-toolkit`, and everything else is a dev dependency. The build is `tsc` for the declarations and Rollup for the bundle, with `prepublishOnly` running the build. The `prebuild` script runs `tsc` and the `build` script runs `rollup -c`, with `rollup.config.mjs` at the root.
The React peer range is `^18.0.0 || ^19.0.0`, and the React types in the dev dependencies are at `^19.2.3`, so the library is tested against React 19 while supporting 18.
The tooling is modern. ESLint at `^9.39.4` with the flat config file `eslint.config.mjs`, `typescript-eslint`, `eslint-plugin-react`, prettier at `^3.5.3` with a `.prettierrc.mjs`, and Rollup 4 with the commonjs, node-resolve, typescript and node-externals plugins. There is a `playground/` directory for local experimentation, a `pnpm-lock.yaml` rather than an npm lockfile, and a `.travis.yml`.
The `fix` script runs prettier and then eslint, and the `prerelease` script runs `npm version prerelease --preid=rc`, which is the release candidate flow.
The release tags and the manifest version do not match
There is a small inconsistency worth flagging, because it affects how you read the version numbers.
The listed releases are v12.0.0 on 2025-01-06, v12.0.2 on 2025-01-06, and 12.1.0 on 2025-06-24. The package manifest in the repository declares version `12.3.0`. So the source tree is ahead of the newest listed tag, and the tag naming is inconsistent too, with one of them lacking the `v` prefix.
That is the ordinary state of a repository where the working tree moves ahead of the last published tag, and it is not a problem in itself. It does mean that a version number in the README badge is not necessarily the version in the manifest, and that if you pin a version you should check what the registry actually has rather than trusting the badge.
The last push to the repository was on 2026-04-19 and the repository is not archived, so the code remains reachable but is not currently receiving regular updates. The README still points at a live demo at `react-resize-detector.vercel.app`, and the npm metrics in the header, the version badge, the likes badge, the downloads badge and the bundle size badge, are the ones the project itself displays.
For an API this small, that is a low risk. The hook's surface is ten props, the underlying mechanism is a native browser API, and the one runtime dependency is a utility library. A library that wraps `ResizeObserver` and nothing else does not accumulate much that can rot.
The licence is MIT, recorded in the manifest and with a `LICENSE` file at the repository root, so it can be used in a commercial product without an obligation to publish anything. That is a licensing observation rather than legal advice, and the `LICENSE` file governs.
Editorial conclusion
react-resize-detector fits a React team that needs element dimensions in JavaScript, for a chart that must redraw at a new width or a layout that cannot be expressed as a container query, and that wants the measurement to arrive as component state rather than as a manual listener. It does not fit a layout that CSS can express, because the project says so itself. Verify first that the element you want to observe is not one whose size changes are already available to you through a container query or a CSS custom property, that the `refreshRate` and `refreshMode` defaults suit your case, since the default is a 1000 millisecond delay, and that the version you install matches the React version in your application, since the peer range covers React 18 and 19.
Frequently asked questions
How do I install react-resize-detector?
Run `npm install react-resize-detector`, or `yarn add react-resize-detector`, or `pnpm add react-resize-detector`. The package peer-depends on React 18 or 19 and ships as an ES module with a single export at `./build/index.js`.
Should I use react-resize-detector or CSS Container Queries?
The project tells you to consider CSS Container Queries first, since they now work in all major browsers and might solve the use case with pure CSS. The library is for JavaScript-based resize logic, complex dimension calculations, integration with React state and effects, or programmatic control over resize behaviour.
What is the default refresh rate for react-resize-detector?
The `refreshRate` prop defaults to `1000` milliseconds and `refreshMode` defaults to `undefined`. The documentation's chart resizing example lowers the rate to 100 and sets the mode to `debounce`, so that a redraw happens once the size settles rather than on every intermediate measurement.
How do I get resize notifications without re-rendering my React component?
Set the `disableRerender` prop to `true`. In that mode the hook will not trigger re-renders and only the `onResize` callback will be called, with a payload whose `width` and `height` are null when the element has unmounted.
What are the peer dependency requirements for react-resize-detector?
React `^18.0.0 || ^19.0.0`. The only runtime dependency of the library itself is `es-toolkit` at `^1.45.1`, which provides the throttle and debounce behaviour behind the `refreshMode` and `refreshOptions` props.
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/maslianok-react-resize-detector)Was this useful?