# use-resize-observer

> A React hook that allows you to use a ResizeObserver to measure an element's size.

Latest version **10.0.0** (published 2026-07-31) · MIT license · 0 weekly downloads

## Install

```sh
npm install use-resize-observer
pnpm add use-resize-observer
yarn add use-resize-observer
bun add use-resize-observer
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 10.0.0 |
| Published | 2026-07-31 |
| First published | 2018-10-28 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 41.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 682 |
| Author | Viktor Hubert |
| Maintainers | zeecoder |
| Keywords | react, hook, react hook, resize observer, resize observer hook, resize observer react hook, use-resize-observer, useresizeobserver, resize hook, size hook, container query, css in js, measure, size, resize |

## Links

- npm: https://www.npmjs.com/package/use-resize-observer
- Repository: https://github.com/ZeeCoder/use-resize-observer
- Homepage: https://github.com/ZeeCoder/use-resize-observer#readme
- Issues: https://github.com/ZeeCoder/use-resize-observer/issues
- npm.io page: https://npm.io/package/use-resize-observer

## Alternatives

- [mobx-react](https://npm.io/package/mobx-react.md) — 2.8M weekly downloads
- [rc-tree](https://npm.io/package/rc-tree.md) — 2.6M weekly downloads
- [@react-oauth/google](https://npm.io/package/@react-oauth/google.md) — 1.3M weekly downloads
- [@wagmi/connectors](https://npm.io/package/@wagmi/connectors.md) — 877.0K weekly downloads
- [vee-validate](https://npm.io/package/vee-validate.md) — 836.4K weekly downloads

## Recent versions

- 10.0.0 (latest) — 2026-07-31
- 10.0.0-alpha.3 (alpha) — 2026-07-31
- 10.0.0-alpha.2 — 2026-07-31
- 10.0.0-alpha.1 — 2026-07-30
- 9.1.0 — 2022-11-22
- 9.1.0-alpha.1 — 2022-11-21
- 9.0.2 — 2022-06-13
- 9.0.0 — 2022-05-15
- 8.0.0 — 2021-08-28
- 7.1.0 — 2021-08-28
- 7.0.1 — 2021-07-27
- 7.0.0 — 2020-11-11
- 7.0.0-alpha.4 — 2020-11-11
- 7.0.0-alpha.3 — 2020-11-11
- 7.0.0-alpha.1 — 2020-10-31
- … 13 more at https://npm.io/package/use-resize-observer/versions

## README

# use-resize-observer


<h1 align="center">
	<br>
	<img width="250" src="https://raw.githubusercontent.com/ZeeCoder/use-resize-observer/master/media/Logo.png" alt="useResizeObserver">
	<br>
    <br>
</h1>

A React hook that allows you to use a ResizeObserver to measure an element's size.

[![npm version](https://badge.fury.io/js/use-resize-observer.svg)](https://npmjs.com/package/use-resize-observer)
[![build](https://github.com/ZeeCoder/use-resize-observer/workflows/Testing/badge.svg)](https://github.com/ZeeCoder/use-resize-observer/actions/workflows/testing.yml)
[![BrowserStack Status](https://automate.browserstack.com/badge.svg?badge_key=aVpjV2RZbThnWnh2S0FvREh0cGRtRHRCNzYwUmw4N0Z4WUxybHM0WkpqST0tLW9RT0tDeGk3OVU2WkNtalpON29xWFE9PQ==--ec6a97c52cd7ad30417612ca3f5df511eef5d631)](https://automate.browserstack.com/public-build/aVpjV2RZbThnWnh2S0FvREh0cGRtRHRCNzYwUmw4N0Z4WUxybHM0WkpqST0tLW9RT0tDeGk3OVU2WkNtalpON29xWFE9PQ==--ec6a97c52cd7ad30417612ca3f5df511eef5d631)

> **Upgrading from v9?** v10 has several breaking changes, see [MIGRATION.md](./MIGRATION.md).

## Highlights

- Written in **TypeScript**.
- **Zero runtime dependencies.**
- **Tiny**: under 1kB (minified, gzipped), monitored by [size-limit](https://github.com/ai/size-limit) ([budget](.size-limit.json)).
- Ships **ESM and CJS** builds.
- Exposes an **onResize callback** if you need more control, receiving the raw
  `ResizeObserverEntry` along with the measured size.
- `box` [option](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#syntax).
- Works with **SSR**.
- Works with **CSS-in-JS**.
- **Supports custom refs** in case you [had one already](#passing-in-your-own-ref).
- **Uses RefCallback by default** to address delayed mounts and changing ref elements.
- Handles many edge cases you might not even think of.
  (See this documentation and the test cases.)
- Easy to compose ([Throttle / Debounce](#throttle--debounce), [Breakpoints](#breakpoints), [Element Coordinates](#element-coordinates))
- **Tested in real browsers** (latest Chrome, Firefox, Edge and Safari, plus real iOS and Android devices, sponsored by BrowserStack)

## Requirements

- **React** 18.2 or newer (the peer range is open-ended, so future majors work too).
- A **`ResizeObserver`** implementation. It is available in all
  [modern browsers](https://caniuse.com/resizeobserver). To support an environment
  without it, either [polyfill it](#polyfilling) yourself, or
  [skip observation entirely](#environments-without-resizeobserver).

## In Action

[CodeSandbox Demo](https://codesandbox.io/s/nrp0w2r5z0)

## Install

```sh
pnpm add use-resize-observer
# or
npm install use-resize-observer
# or
yarn add use-resize-observer
```

## Options

| Option   | Type                                                                                 | Description                                                                                                                   | Default        |
| -------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | -------------- |
| ref      | undefined &#124; RefObject &#124; Element                                             | A ref or element to observe. If omitted, use the [ref callback](#response) the hook returns instead (preferred).             | undefined      |
| box      | undefined &#124; "border-box" &#124; "content-box" &#124; "device-pixel-content-box" | The [box model](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#syntax) to use for observation.       | "content-box"  |
| onResize | undefined &#124; ({ width, height, entry }: [ResizeHandlerPayload](#the-raw-entry)) => void | A callback receiving the element size and the [raw entry](#the-raw-entry). Providing it opts into **callback-only mode**: the hook stops returning/updating `width` & `height` and no longer re-renders — you own updates (and the render optimisation) instead. | undefined      |
| round    | undefined &#124; (n: number) => number                                               | A function to use for rounding values instead of the default.                                                                 | `Math.round()` |

## Response

| Name   | Type                    | Description                                    |
| ------ | ----------------------- | ---------------------------------------------- |
| ref    | RefCallback             | A callback to be passed to React's "ref" prop. |
| width  | undefined &#124; number | The width (or "inlineSize") of the element.     |
| height | undefined &#124; number | The height (or "blockSize") of the element.   |

## Basic Usage

`useResizeObserver` is a **named** export:

```tsx
import { useResizeObserver } from "use-resize-observer";

const App = () => {
  const { ref, width = 1, height = 1 } = useResizeObserver<HTMLDivElement>();

  return (
    <div ref={ref}>
      Size: {width}x{height}
    </div>
  );
};
```

To observe a different box size other than content box, pass in the `box` option, like so:

```tsx
const { ref, width, height } = useResizeObserver<HTMLDivElement>({
  box: "border-box",
});
```

Note that if the browser does not support the given box type, then the hook won't report any sizes either.

### Box Options

Note that box options are experimental, and as such are not supported by all browsers that implemented ResizeObservers. (See [here](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry).)

`content-box` (default)

Safe to use by all browsers that implemented ResizeObservers. The hook internally will fall back to `contentRect` from
the old spec in case `contentBoxSize` is not available.

`border-box`

Supported well for the most part by evergreen browsers. If you need to support older versions of these browsers however,
then you may want to feature-detect for support.

`device-pixel-content-box`

Surma has a [very good article](https://web.dev/device-pixel-content-box/) on how this allows us to do pixel perfect
rendering. At the time of writing, however, this has limited support (notably, it is unsupported in Safari).
Feature-detect before relying on it.

### Custom Rounding

By default this hook passes the measured values through `Math.round()`, to avoid re-rendering on every subpixel changes.

If this is not what you want, then you can provide your own function:

**Rounding Down Reported Values**

```tsx
const { ref, width, height } = useResizeObserver<HTMLDivElement>({
  round: Math.floor,
});
```

**Skipping Rounding**

```tsx
import { useResizeObserver } from "use-resize-observer";

// Outside the hook to ensure this instance does not change unnecessarily.
const noop = (n: number) => n;

const App = () => {
  const {
    ref,
    width = 1,
    height = 1,
  } = useResizeObserver<HTMLDivElement>({ round: noop });

  return (
    <div ref={ref}>
      Size: {width}x{height}
    </div>
  );
};
```

Note that the round option is sensitive to the function reference, so make sure you either use `useCallback`
or declare your rounding function outside of the hook's function scope, if it does not rely on any hook state.
(As shown above.)

### Getting the Raw Element from the Default `RefCallback`

Note that "ref" in the above examples is a `RefCallback`, not a `RefObject`, meaning you won't be
able to access "ref.current" if you need the element itself.

To get the raw element, either you use your own RefObject (see later in this doc),
or you can merge the returned ref with one of your own:

```tsx
import { useResizeObserver } from "use-resize-observer";
import { mergeRefs } from "react-merge-refs";

const App = () => {
  const { ref, width = 1, height = 1 } = useResizeObserver<HTMLDivElement>();

  const mergedCallbackRef = mergeRefs([
    ref,
    (element: HTMLDivElement) => {
      // Do whatever you want with the `element`.
    },
  ]);

  return (
    <div ref={mergedCallbackRef}>
      Size: {width}x{height}
    </div>
  );
};
```

## Passing in Your Own `ref`

Where you can, prefer the `RefCallback` the hook returns (the default usage shown
above) — it handles delayed mounts and elements that change over time. Passing
your own ref is for when you already have one you need to measure.

```ts
const ref = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver<HTMLDivElement>({ ref });
```

You can even reuse the same hook instance to measure different elements:

[CodeSandbox Demo](https://codesandbox.io/s/use-resize-observer-reusing-refs-buftd)

## Measuring a raw element

There might be situations where you have an element already that you need to measure.
The `ref` option accepts a raw element too, not just a ref, so you can do this:

```ts
const { width, height } = useResizeObserver<HTMLDivElement>({
  ref: divElement,
});
```

Elements from another window (for example a cross-document iframe) are supported too.

## Using a Single Hook to Measure Multiple Refs

The hook reacts to ref changes, as it resolves it to an element to observe.
This means that you can freely change the custom `ref` option from one ref to
another and back, and the hook will start observing whatever is set in its options.

## Opting Out of (or Delaying) ResizeObserver Instantiation

In certain cases you might want to delay creating a ResizeObserver instance.

You might provide a library, that only optionally provides observation features
based on props, which means that while you have the hook within your component,
you might not want to actually initialise it.

Another example is that you might want to entirely opt out of initialising, when
you run some tests, where the environment does not provide the `ResizeObserver`.

([See discussions](https://github.com/ZeeCoder/use-resize-observer/issues/40))

Use the default `ref` RefCallback, or provide a custom ref conditionally, only
when needed. The hook will not create a ResizeObserver instance until there's
something there to actually observe.

## The "onResize" Callback

By the default the hook will trigger a re-render on all changes to the target
element's width and / or height.

You can opt out of this behaviour, by providing an `onResize` callback function,
which'll simply receive the width and height of the element when it changes, so
that you can decide what to do with it:

```tsx
import { useResizeObserver } from "use-resize-observer";

const App = () => {
  // width / height will not be returned here when the onResize callback is present
  const { ref } = useResizeObserver<HTMLDivElement>({
    onResize: ({ width, height }) => {
      // do something here.
    },
  });

  return <div ref={ref} />;
};
```

### The raw `entry`

Along with the resolved `width` / `height`, the callback receives the raw
[ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
as `entry`. This gives you everything the hook itself doesn't surface:

- **All box sizes**, regardless of the `box` option in use. Browsers report
  `contentBoxSize`, `borderBoxSize` and `devicePixelContentBoxSize` on every entry
  (where supported), so you can read a different box than the one being observed.
- **The observed element**, as `entry.target`. This is handy when you use the
  returned ref callback, where the element isn't otherwise at hand in the callback.
  It also lets you reach for things the observer doesn't provide at all, like the
  element's [coordinates](#element-coordinates).

```tsx
const { ref } = useResizeObserver<HTMLDivElement>({
  onResize: ({ width, height, entry }) => {
    // e.g. the element itself, and the border box while observing the content box:
    console.log(entry.target, entry.borderBoxSize);
  },
});
```

This callback also makes it possible to implement your own hooks that report only
what you need, for example:

- Reporting only width or height
- Throttle / debounce
- Wrap in `requestAnimationFrame`

## Hook Composition

As this hook intends to remain low-level, it is encouraged to build on top of it via hook composition, if additional features are required.

### Throttle / Debounce

You might want to receive values less frequently than changes actually occur.

[CodeSandbox Demo](https://codesandbox.io/s/use-resize-observer-throttle-and-debounce-8uvsg)

### Breakpoints

Another popular concept are breakpoints. Here is an example for a simple hook accomplishing that.

[CodeSandbox Demo](https://codesandbox.io/s/use-resize-observer-breakpoints-3hiv8)

### Element Coordinates

The hook reports sizes only. If you need the element's `x` / `y` / `top` / `left`
too, read them off the element with
[getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect),
which the [raw `entry`](#the-raw-entry) puts within easy reach:

```ts
const { ref } = useResizeObserver({
  onResize: ({ entry }) => {
    requestAnimationFrame(() => {
      const rect = entry.target.getBoundingClientRect();
      // ... do something with the result ...
    });
  },
});
```

`getBoundingClientRect()` forces a layout, so it is wrapped in a
`requestAnimationFrame` here to avoid
[layout thrashing](https://github.com/ZeeCoder/use-resize-observer/discussions/102#discussioncomment-5020345).
Only reach for this if you actually need the coordinates — if width / height is
all you're after, use the hook as normal and leave `entry` alone.

## Defaults (SSR)

On initial mount the ResizeObserver will take a little time to report on the
actual size.

Until the hook receives the first measurement, it returns `undefined` for width
and height by default.

You can override this behaviour, which could be useful for SSR as well.

```ts
const { ref, width = 100, height = 50 } = useResizeObserver<HTMLDivElement>();
```

Here "width" and "height" will be 100 and 50 respectively, until the
ResizeObserver kicks in and reports the actual size.

## Without Defaults

If you only want real measurements (only values from the ResizeObserver without
any default values), then you can just leave defaults off:

```ts
const { ref, width, height } = useResizeObserver<HTMLDivElement>();
```

Here "width" and "height" will be undefined until the ResizeObserver takes its
first measurement. A measured dimension of `0` is reported as `0` (not
`undefined`), so you can distinguish a genuinely zero-sized element from one that
has not been measured yet.

## Container/Element Query with CSS-in-JS

It's possible to apply styles conditionally based on the width / height of an
element using a CSS-in-JS solution, which is the basic idea behind
container/element queries:

[CodeSandbox Demo](https://codesandbox.io/s/use-resize-observer-container-query-with-css-in-js-iitxl)

## Polyfilling

The library targets modern (ES2020) browsers and ships **no** ResizeObserver
polyfill. It has zero runtime dependencies.

Polyfilling is best done in the host app, and not within imported libraries, as
that way consumers control the exact polyfills being used. If you need to support
an environment without a native `ResizeObserver`, install a polyfill such as
[@juggle/resize-observer](https://github.com/juggle/resize-observer) and make it
available before the hook runs — for example in your app's entry point:

```ts
import { ResizeObserver } from "@juggle/resize-observer";

if (!window.ResizeObserver) {
  window.ResizeObserver = ResizeObserver;
}
```

## Environments Without `ResizeObserver`

Polyfilling is one option. The other is to let the hook sit idle where
`ResizeObserver` is missing, and fall back to default sizes.

This works because the hook creates its `ResizeObserver` **lazily** — not on
render, but the first time it actually has an element to observe. (That is what
makes it SSR-safe too.) Give it nothing to observe and it never touches the
global, so nothing throws. Hooks can't be called conditionally, but this way you
don't need to.

Detect the global once, then wrap the returned ref callback so the element is only
passed through when it's there:

```ts
const isRoAvailable = typeof window !== "undefined" && "ResizeObserver" in window;

// Stays at 100x50 where there's no ResizeObserver to measure with.
const { ref: observe, width = 100, height = 50 } = useResizeObserver<HTMLDivElement>();

const ref = useCallback(
  (element: HTMLDivElement | null) => {
    if (isRoAvailable) {
      observe(element);
    }
  },
  [observe],
);
```

The same works with a ref object, if you have one already — hand it to the hook
only when the global is available:

```ts
const ref = useRef<HTMLDivElement>(null);
const { width = 100, height = 50 } = useResizeObserver<HTMLDivElement>({
  ref: isRoAvailable ? ref : null,
});
```

## "ResizeObserver loop limit exceeded"

If you come across this — `ResizeObserver loop limit exceeded` in Chrome, or
`ResizeObserver loop completed with undelivered notifications` in Firefox — it is
**harmless**, despite surfacing as an error.

It means observing caused a resize, which caused another observation, and the
browser cut the loop short instead of hanging. It's a guard against infinite loops
during a layout pass, not a crash — the pending notifications are simply delivered
on the next frame.

Worth addressing all the same, in this order:

1. **Find the root cause.** Usually something in your resize handling changes
   layout in a way that feeds back into the observed element. That feedback loop
   is the actual bug, and fixing it makes the message go away for good.
2. **Filter it out in your bug tracker** (Sentry, Datadog, etc). If you can't pin
   it down, stop collecting it. It's noise rather than a failure, and left alone
   it can bury real errors.
3. **Defer reporting by a frame.** A last resort — compose a hook that pushes the
   size out through `requestAnimationFrame`:

```ts
const useResizeObserverWithRAF = (opts) => {
  const [size, setSize] = useState({ width: undefined, height: undefined });

  const { ref } = useResizeObserver({
    ...opts,
    onResize: ({ width, height }) => {
      requestAnimationFrame(() => setSize({ width, height }));
    },
  });

  return { ref, ...size };
};
```

This works because it moves the reporting out of the layout pass where the limit
is enforced. The catch is that sizes now arrive a frame late (~16ms at 60fps),
which gives up much of the point of using a `ResizeObserver` — so reach for it
only once the first two options are exhausted.

## Related

- [@zeecoder/container-query](https://github.com/ZeeCoder/container-query)
- [@zeecoder/react-resize-observer](https://github.com/ZeeCoder/react-resize-observer)

## License

MIT

---
_Source: https://npm.io/package/use-resize-observer · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
