Our website uses cookies.
Reject AllAllow all

This website stores cookies on your computer. The data is used to collect information about how you interact with our website and allow us to remember you. We use this information to improve and customize your browsing experience and for analytics and metrics about our visitors both on this website and other media.

Measuring React Elements

So today I'm going to share with you a nice code snippet that we use for getting the dimensions of a React element.

What's so nice about it? It's a drop-in-and-forget solution, performant, not-so-large, doesn't need any external libraries, aaand…. is Server Side Rendering compatible, so you can use it safely with Next.js.

import { useCallback, useLayoutEffect, useState } from "react";

export function useElementSize() {
  const [size, setSize] = useState<[number, number]>([0, 0]);
  const [elementRef, setElementRef] = useState<HTMLElement | null>(null);

  const ref = useCallback((element: HTMLElement | null) => {
    setElementRef(element)
  }, []);

  useLayoutEffect(() => {
    const el = elementRef;

    const resizeObserver = new ResizeObserver((entries) => {
      const entry = entries[0]
      if (entry) {
        setSize([entry.contentRect.width, entry.contentRect.height]);
      }
    });
    if (el) resizeObserver.observe(el);

    return () => {
      if (el) resizeObserver.unobserve(el);
    };
  }, [elementRef]);

  return { size, ref };
}

So let's walk through what is actually happening there.

  1. First interesting part begins with handling the ref. The pattern you would mostly often go with is just passing the ref as an argument to the hook… but it's not the case there, we want to separate the developer from having to initalize the ref, so we keep the reference to the element inside state (in order to trigger the layout effect and reinitialize the ResizeObserver if the element changed), and define our custom ref-function (take a look at that useCallback :)).
  2. Then the useLayoutEffect. First of all, we use it because we want to do all the measurements just after all the DOM mutations finish, which makes up for a better feel of "snappiness". Then we initialize a quite simple ResizeObserver which, suprise, observes the element resizing, and updates our measurements when needed.

You can modify this hook as you want to your needs - maybe you want to perform some other calculations than just the element size?

Also, take into consideration that you might want to use a polyfill for ResizeObserver, as it's not supported in all the browsers :)

Aaaand that's it, hope you learned something, I'll also leave some useful links below:

  • Working example: https://codesandbox.io/s/awesome-gould-49hw4s
  • useEffect vs. useLayoutEffect: https://kentcdodds.com/blog/useeffect-vs-uselayouteffect
  • ResizeObserver API: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
  • ResizeObserver Polyfill: https://www.npmjs.com/package/resize-observer-polyfill
  • https://caniuse.com/resizeobserver