useResizeObserver


Custom hook that observes the size of an element using the ResizeObserver API.

Usage

import { useRef, useState } from 'react'  import { useDebounceCallback, useResizeObserver } from 'usehooks-ts'  type Size = {  width?: number  height?: number }  export default function Component() {  const ref = useRef<HTMLDivElement>(null)  const { width = 0, height = 0 } = useResizeObserver({  ref,  box: 'border-box',  })   return (  <div ref={ref} style={{ border: '1px solid palevioletred', width: '100%' }}>  {width} x {height}  </div>  ) }  export function WithDebounce() {  const ref = useRef<HTMLDivElement>(null)  const [{ width, height }, setSize] = useState<Size>({  width: undefined,  height: undefined,  })   const onResize = useDebounceCallback(setSize, 200)   useResizeObserver({  ref,  onResize,  })   return (  <div  ref={ref}  style={{  border: '1px solid palevioletred',  width: '100%',  resize: 'both',  overflow: 'auto',  maxWidth: '100%',  }}  >  debounced: {width} x {height}  </div>  ) } 

API

useResizeObserver<T>(options): Size

Custom hook that observes the size of an element using the ResizeObserver API.

Type parameters

NameTypeDescription
Textends HTMLElement = HTMLElementThe type of the element to observe.

Parameters

NameTypeDescription
optionsUseResizeObserverOptions<T>The options for the ResizeObserver.

Returns

Size

  • The size of the observed element.

Type aliases

Ƭ Size: Object

The size of the observed element.

Type declaration

NameTypeDescription
heightnumber | undefinedThe height of the observed element.
widthnumber | undefinedThe width of the observed element.

Ƭ UseResizeObserverOptions<T>: Object

The options for the ResizeObserver.

Type parameters

NameType
Textends HTMLElement = HTMLElement

Type declaration

NameTypeDescription
box?"border-box" | "content-box" | "device-pixel-content-box"The box model to use for the ResizeObserver. Default ts 'content-box'
onResize?(size: Size) => voidWhen using onResize, the hook doesn't re-render on element size changes; it delegates handling to the provided callback. Default ts undefined
refRefObject<T>The ref of the element to observe.

Hook

import { useEffect, useRef, useState } from 'react'  import type { RefObject } from 'react'  import { useIsMounted } from 'usehooks-ts'  type Size = {  width: number | undefined  height: number | undefined }  type UseResizeObserverOptions<T extends HTMLElement = HTMLElement> = {  ref: RefObject<T>  onResize?: (size: Size) => void  box?: 'border-box' | 'content-box' | 'device-pixel-content-box' }  const initialSize: Size = {  width: undefined,  height: undefined, }  export function useResizeObserver<T extends HTMLElement = HTMLElement>(  options: UseResizeObserverOptions<T>, ): Size {  const { ref, box = 'content-box' } = options  const [{ width, height }, setSize] = useState<Size>(initialSize)  const isMounted = useIsMounted()  const previousSize = useRef<Size>({ ...initialSize })  const onResize = useRef<((size: Size) => void) | undefined>(undefined)  onResize.current = options.onResize   useEffect(() => {  if (!ref.current) return   if (typeof window === 'undefined' || !('ResizeObserver' in window)) return   const observer = new ResizeObserver(([entry]) => {  const boxProp =  box === 'border-box'  ? 'borderBoxSize'  : box === 'device-pixel-content-box'  ? 'devicePixelContentBoxSize'  : 'contentBoxSize'   const newWidth = extractSize(entry, boxProp, 'inlineSize')  const newHeight = extractSize(entry, boxProp, 'blockSize')   const hasChanged =  previousSize.current.width !== newWidth ||  previousSize.current.height !== newHeight   if (hasChanged) {  const newSize: Size = { width: newWidth, height: newHeight }  previousSize.current.width = newWidth  previousSize.current.height = newHeight   if (onResize.current) {  onResize.current(newSize)  } else {  if (isMounted()) {  setSize(newSize)  }  }  }  })   observer.observe(ref.current, { box })   return () => {  observer.disconnect()  }  }, [box, ref, isMounted])   return { width, height } }  type BoxSizesKey = keyof Pick<  ResizeObserverEntry,  'borderBoxSize' | 'contentBoxSize' | 'devicePixelContentBoxSize' >  function extractSize(  entry: ResizeObserverEntry,  box: BoxSizesKey,  sizeType: keyof ResizeObserverSize, ): number | undefined {  if (!entry[box]) {  if (box === 'contentBoxSize') {  return entry.contentRect[sizeType === 'inlineSize' ? 'width' : 'height']  }  return undefined  }   return Array.isArray(entry[box])  ? entry[box][0][sizeType]  : // @ts-ignore Support Firefox's non-standard behavior  (entry[box][sizeType] as number) }