Skip to content
Swizec Teller - a geek with a hatswizec.com

Use ref callbacks to measure React component size

You can use ref callbacks to measure the rendered size of React components, did you know? It's a neat little trick.

Here's how it works ๐Ÿ‘‡ 1. React renders your component 2. Browser layout engine does its thing 3. ref callback fires 4. Use getBoundingClientRect to measure element size 5. Use this info for whatever you want

We used this trick in this Dynamic SVG accordion example and in this Tiny React & D3 flamegraph tutorial. That's because layouting in SVG is hard, and you have to do everything yourself.

Using ref callbacks to measure your elements is a little less useful in the modern HTML + CSS world. You can use flexbox and css-grid so you never need to know what you're dealing with.

And yet when push comes to shove, sometimes you just really need your code to know the size of an element.

A minimal size reporting component looks like this ๐Ÿ‘‡

class ReportSize extends React.Component {
refCallback = (element) => {
if (element) {
this.props.getSize(element.getBoundingClientRect());
}
};
render() {
return (
<div ref={this.refCallback} style={{ border: "1px solid red" }}>
{faker.lorem.paragraphs(Math.random() * 10)}
</div>
);
}
}

The render method outputs a <div> with a ref callback and a red border. Inside, we use faker to generate up to 10 random paragraphs.

After React places this element, it calls refCallback with a reference to the rendered DOM node. We can then use getBoundingClientRect to measure its size.

{
"x": 8,
"y": 158.8125,
"width": 544,
"height": 340,
"top": 158.8125,
"right": 552,
"bottom": 498.8125,
"left": 8
}

All sorts of useful info!

So why not just use componentDidMount?

Yes, that works too. But it's less elegant because you have to save the ref first. The refCallback API calls your function with a nice reference already packaged in.

However, you might still have to do that if your component size changes after initial render. Observe ๐Ÿ‘‡

Clicking the shuffle button doesn't report new sizing information up the hierarchy. That's not good ๐Ÿค”

If your component changes size without re-mounting, you have to re-measure its size in componentDidUpdate as well. But that way lies troubleโ€ฆ you can fall into the infinite recursion trap.

You can solve the problem with a lock, like this ๐Ÿ‘‡

Keep clicking shuffle and sizing info is always correct.

The key is enabling size reporting when you logically know size is going to change, in shuffle, and disabling it as soon as you report the change in componentDidUpdate.

shuffle = () => {
this.doReportSize = true;
this.setState({
text: faker.lorem.paragraphs(Math.random() * 10)
});
};
refCallback = element => {
if (element) {
this.elementRef = element;
this.props.getSize(element.getBoundingClientRect());
}
};
componentDidUpdate() {
if (this.doReportSize) {
this.props.getSize(this.elementRef.getBoundingClientRect());
this.doReportSize = false;
}
}

Oof, not pretty. Setting a this.elementRef in our callback, messing around with class properties for flags. Pretty sure we could've just used the new React.createRef() API in combination with componentDidMount and componentDidUpdate.

Would still need the flag to prevent infinite loops, however.

I wonder if using class properties to make render flags like that will continue to work when asynchronous rendering comes with React 17 ๐Ÿค”

Did you enjoy this article?

Published on June 7th, 2018 in Front End, Technical

Learned something new?
Want to become a high value JavaScript expert?

Here's how it works ๐Ÿ‘‡

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet ๐Ÿ“–right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

Start with an interactive cheatsheet ๐Ÿ“–

Then get thoughtful letters ๐Ÿ’Œ on mindsets, tactics, and technical skills for your career.

"Man, love your simple writing! Yours is the only email I open from marketers and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. ๐Ÿ‘Œ"

~ Ashish Kumar

Join over 10,000 engineers just like you already improving their JS careers with my letters, workshops, courses, and talks. โœŒ๏ธ

Have a burning question that you think I can answer?ย I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

Ready to Stop copy pasting D3 examples and create data visualizations of your own? ย Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first ๐Ÿ’ฐ on the side with ServerlessReact.Dev

Want to brush up on your modern JavaScript syntax?ย Check out my interactive cheatsheet: es6cheatsheet.com

By the way, just in case no one has told you it yet today: I love and appreciate you for who you areย โค๏ธ

Created bySwizecwith โค๏ธswizec.com