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 };
}
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
:)).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: