Detect width and height changes in React.js

Sometimes we need to get access to a react component's width and height properties. In this post we will explore how to create a react hook, which dynamically returns these values on resizing.

Sometimes we need to get access to a react component's width and height properties. In this post, we'll see how we can create a react hook, which dynamically gives us these values on resizing.

What we want to achieve

As a simple example, we want to create a responsive div displaying its own dimensions upon resizing.

div element displaying it's width of 509px and height of 177px in text

When the window resizes, the dimensions of our div will also change. responsive div element displaying it's width of 348px and height of 177px after resizing in text

Our Building Blocks

Our skeleton for this example is a responsive div with hardcoded values for displaying both width and height.

export default function Home() {
  const divWidth = 348
  const divHeight = 177
  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      <div
        style={{
          margin: '50px',
          width: '70%',
          height: '70%',
          border: '1px solid black',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        Dimensions: {divWidth}w {divHeight}h
      </div>
    </div>
  )
}

<!-- We will make use of two main building blocks for our hook: 1. [React Refs](https://reactjs.org/docs/refs-and-the-dom.html) Refs are a way in React.js to access DOM nodes. Our resize hook will use refs to get access to the resizing div element. 2. [useLayoutEffect](https://reactjs.org/docs/refs-and-the-dom.html) This hook is similar to `useEffect`, but fires after all DOM mutations. We will use it to attach our resize listener to the resizing div element. -->

Getting access to the div with refs

We begin by adding a React Ref to the div element.

export default function Home() {
  const divRef = createRef()
  // ...
  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      <div
        ref={divRef} // ...
      ></div>
    </div>
  )
}

With this divRef we are now able to access the underlying DOM element from our react code via divRef.current.

Creating a hook to return dimensions

Next, we'll want to replace the hard-coded values divWidth and divHeight with values returned by a hook. Let's start by creating a dummy hook.

import React, { useState, createRef } from 'react'

const useRefDimensions = (ref) => {
  const [dimensions, setDimensions] = useState({ width: 1, height: 2 })
  return dimensions
}

export default function Home() {
  const divRef = createRef()
  const dimensions = useRefDimensions(divRef)
  // ...
  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      <div
        ref={divRef} // ...
      >
        Dimensions: {dimensions.width}w {dimensions.height}h
      </div>
    </div>
  )
}

Getting ref dimensions in the react hook

With our ref and hook in place, we are ready to add logic to our hook to analyze the div's dimensions and return those dynamically via the getBoundingClientRect() method on the DOM element.

const useRefDimensions = (ref) => {
  const [dimensions, setDimensions] = useState({ width: 1, height: 2 })
  React.useEffect(() => {
    if (ref.current) {
      const { current } = ref
      const boundingRect = current.getBoundingClientRect()
      const { width, height } = boundingRect
      setDimensions({ width: Math.round(width), height: Math.round(height) })
    }
  }, [ref])
  return dimensions
}

By using an Effect Hook within our hook we calculate and return the ref's dimensions on every resize.

Solution

import React, { useState, createRef } from 'react'

const useRefDimensions = (ref) => {
  const [dimensions, setDimensions] = useState({ width: 1, height: 2 })
  React.useEffect(() => {
    if (ref.current) {
      const { current } = ref
      const boundingRect = current.getBoundingClientRect()
      const { width, height } = boundingRect
      setDimensions({ width: Math.round(width), height: Math.round(height) })
    }
  }, [ref])
  return dimensions
}

export default function Home() {
  const divRef = createRef()
  const dimensions = useRefDimensions(divRef)

  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      <div
        ref={divRef}
        style={{
          margin: '50px',
          width: '70%',
          height: '70%',
          border: '1px solid black',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        Dimensions: {dimensions.width}w {dimensions.height}h
      </div>
    </div>
  )
}