Powered byAppUnite LogoBlog
measuring.jpg

Measuring React Elements

| 2 min read

measuring.jpg

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: