How to get the width of a react element

Reactjs

Reactjs Problem Overview


Im trying to create a range input that displays a tooltip right above the slider thumb.

I went through some vanilla JS examples online and it seems that I need to have the width of the element to acomplish that.

So I was just wondering how to get the elements width?

Pretty much the equivalent of the JQuery method $(element).width()

Reactjs Solutions


Solution 1 - Reactjs

    class MyComponent extends Component {
      constructor(props){
        super(props)
        this.myInput = React.createRef()
      }

      componentDidMount () {
        console.log(this.myInput.current.offsetWidth)
      }
    
      render () {
        return (
        // new way - as of [email protected]
        <div ref={this.myInput}>some elem</div>
        // legacy way
        // <div ref={(ref) => this.myInput = ref}>some elem</div>
        )
      }
    }

Solution 2 - Reactjs

With hooks:

const MyComponent = () => {
  const ref = useRef(null);
  useEffect(() => {
    console.log('width', ref.current ? ref.current.offsetWidth : 0);
  }, [ref.current]);
  return <div ref={ref}>Hello</div>;
};

Solution 3 - Reactjs

This is basically Marco Antônio's answer for a React custom hook, but modified to set the dimensions initially and not only after a resize.

export const useContainerDimensions = myRef => {
  const getDimensions = () => ({
    width: myRef.current.offsetWidth,
    height: myRef.current.offsetHeight
  })

  const [dimensions, setDimensions] = useState({ width: 0, height: 0 })

  useEffect(() => {
    const handleResize = () => {
      setDimensions(getDimensions())
    }

    if (myRef.current) {
      setDimensions(getDimensions())
    }

    window.addEventListener("resize", handleResize)

    return () => {
      window.removeEventListener("resize", handleResize)
    }
  }, [myRef])

  return dimensions;
};

Used in the same way:

const MyComponent = () => {
  const componentRef = useRef()
  const { width, height } = useContainerDimensions(componentRef)

  return (
    <div ref={componentRef}>
      <p>width: {width}px</p>
      <p>height: {height}px</p>
    <div/>
  )
}

Solution 4 - Reactjs

Actually, would be better to isolate this resize logic in a custom hook. You can create a custom hook like this:

const useResize = (myRef) => {
  const [width, setWidth] = useState(0)
  const [height, setHeight] = useState(0)

  useEffect(() => {
    const handleResize = () => {
      setWidth(myRef.current.offsetWidth)
      setHeight(myRef.current.offsetHeight)
    }

    window.addEventListener('resize', handleResize)

    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [myRef])

  return { width, height }
}

and then you can use it like:

const MyComponent = () => {
  const componentRef = useRef()
  const { width, height } = useResize(componentRef)

  return (
    <div ref={myRef}>
      <p>width: {width}px</p>
      <p>height: {height}px</p>
    <div/>
  )
}

Solution 5 - Reactjs

A simple and up to date solution is to use the React React useRef hook that stores a reference to the component/element, combined with a useEffect hook, which fires at component renders.

import React, {useState, useEffect, useRef} from 'react';

export default App = () => {
  const [width, setWidth] = useState(0);
  const elementRef = useRef(null);

  useEffect(() => {
    setWidth(elementRef.current.getBoundingClientRect().width);
  }, []); //empty dependency array so it only runs once at render

  return (
    <div ref={elementRef}>
      {width}
    </div>
  )
}

Solution 6 - Reactjs

Here is a TypeScript version of @meseern's answer that avoids unnecessary assignments on re-render:

import React, { useState, useEffect } from 'react';

export function useContainerDimensions(myRef: React.RefObject<any>) {
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

  useEffect(() => {
    const getDimensions = () => ({
      width: (myRef && myRef.current.offsetWidth) || 0,
      height: (myRef && myRef.current.offsetHeight) || 0,
    });

    const handleResize = () => {
      setDimensions(getDimensions());
    };

    if (myRef.current) {
      setDimensions(getDimensions());
    }

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [myRef]);

  return dimensions;
}

Solution 7 - Reactjs

Just to complement on Christopher's comment. You can use the 'react-use' library to do this. It also listens when the browser resizes. For reference: https://github.com/streamich/react-use/blob/master/docs/useMeasure.md

import React from 'react';

import { useMeasure } from 'react-use';

const sampleView = () => {
 const [ref, { width }] = useMeasure<HTMLDivElement>();
 console.log('Current width of element', width);
 return <div ref={ref}></div>;
};

export default sampleView;

Solution 8 - Reactjs

This could be handled perhaps in a simpler way by using callback refs.

React allows you to pass a function into a ref, which returns the underlying DOM element or component node. See: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

const MyComponent = () => {
	const myRef = node => console.log(node ? node.innerText : 'NULL!');
	return <div ref={myRef}>Hello World</div>;
 }

This function gets fired whenever the underlying node is changed. It will be null in-between updates, so we need to check for this. Example:

const MyComponent = () => {
	const [time, setTime] = React.useState(123);
	const myRef = node => console.log(node ? node.innerText : 'NULL!');
	setTimeout(() => setTime(time+1), 1000);
	return <div ref={myRef}>Hello World {time}</div>;
}
/*** Console output: 
 Hello World 123
 NULL!
 Hello World 124
 NULL!
...etc
***/

While this does't handle resizing as such (we would still need a resize listener to handle the user resizing the window) I'm not sure that is what the OP was asking for. And this version will handle the node resizing due to an update.

So here is a custom hook based on this idea:

export const useClientRect = () => {
	const [rect, setRect] = useState({width:0, height:0});
	const ref = useCallback(node => {
		if (node !== null) {
			const { width, height } = node.getBoundingClientRect();
			setRect({ width, height });
		}
	}, []);
	return [rect, ref];
};

The above is based on https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node

Note the hook returns a ref callback, instead of being passed a ref. And we employ useCallback to avoid re-creating a new ref function each time; not vital, but considered good practice.

Usage is like this (based on Marco Antônio's example):

const MyComponent = ({children}) => {
  const [rect, myRef] = useClientRect();
  const { width, height } = rect;

  return (
    <div ref={myRef}>
      <p>width: {width}px</p>
      <p>height: {height}px</p>
	  {children}
    <div/>
  )
}

Solution 9 - Reactjs

React Hook:

import React, { useState, useEffect,useRef } from 'react';
...
const table1ref = useRef(null);
const [table1Size, table1SizeSet] = useState({
  width: undefined,
  height: undefined,
});

useEffect(() => {
    function handleResize() {
      table1SizeSet({
        width: table1ref.current.offsetWidth,
        height: table1ref.current.offsetHeight,
      });
    }
    window.addEventListener("resize", handleResize);
    handleResize();
    return () => window.removeEventListener("resize", handleResize);        
  }, [ ]);
...
<div  ref={table1ref}>

And call :

{table1Size.width}

When you want to use.

Solution 10 - Reactjs

A good practice is listening for resize events to prevent resize on render or even a user window resize that can bug your application.

const MyComponent = ()=> {
  const myRef = useRef(null)

  const [myComponenetWidth, setMyComponentWidth] = useState('')

  const handleResize = ()=>{ 
    setMyComponentWidth(myRef.current.offsetWidth)
  }

  useEffect(() =>{
    if(myRef.current)
      myRef.current.addEventListener('resize', handleResize)

    return ()=> {
     myRef.current.removeEventListener('resize', handleResize)
    }
  }, [myRef])

  return (
  <div ref={MyRef}>Hello</div>
  )
}

Solution 11 - Reactjs

There is a library, use-resize-observer, giving you a hook built around ResizeObserver.

import React from "react";
import useResizeObserver from "use-resize-observer";

const App = () => {
  const { ref, width, height } = useResizeObserver<HTMLDivElement>();

  return (
    <div>
      <div className="instructions">Try resizing this div!</div>
      <div ref={ref} className="box">
        {width}x{height}
      </div>
    </div>
  );
};

Solution 12 - Reactjs

custom hook

import { useCallback, useState } from "react";

const useContainerDimensions = () => {
  const [size, setRect] = useState(null);
  const ref = useCallback(node => {
    if (node !== null) {
      setRect(node.getBoundingClientRect());
    }
  }, []);
  return [size, ref];
}
export default useContainerDimensions;

and then you can use it like

const [size, ref] = useContainerDimensions();

return (
 <Text ellipsis style={{maxWidth: size?.width}}>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod 
  tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim 
  veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea 
  commodo consequat. Duis aute irure dolor in reprehenderit in voluptate 
  velit esse cillum dolore
 </Text>
)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRick EncisoView Question on Stackoverflow
Solution 1 - ReactjsRick EncisoView Answer on Stackoverflow
Solution 2 - ReactjsNeluView Answer on Stackoverflow
Solution 3 - ReactjsmeesernView Answer on Stackoverflow
Solution 4 - ReactjsMarco AntônioView Answer on Stackoverflow
Solution 5 - ReactjscharriView Answer on Stackoverflow
Solution 6 - ReactjsaptlinView Answer on Stackoverflow
Solution 7 - ReactjsDominic J. LucenarioView Answer on Stackoverflow
Solution 8 - ReactjsTrevedhekView Answer on Stackoverflow
Solution 9 - ReactjsTonyView Answer on Stackoverflow
Solution 10 - ReactjsLuís Guilherme Pelin MartinsView Answer on Stackoverflow
Solution 11 - ReactjsFateme NorouziView Answer on Stackoverflow
Solution 12 - ReactjsFateme NorouziView Answer on Stackoverflow