REACT COOL DIMENSIONS
A React hook that measure an element's size and handle responsive components with highly-performant way, using ResizeObserver. Try it you will 👍🏻 it!
❤️ it? ⭐️ it on GitHub or Tweet about it.
⚡️ Try yourself: https://react-cool-dimensions.netlify.app
Features
Requirement
To use react-cool-dimensions
, you must use react@16.8.0
or greater which includes hooks.
Installation
This package is distributed via npm.
$ yarn add react-cool-dimensions
$ npm install --save react-cool-dimensions
Usage
react-cool-dimensions
has a flexible API design, it can cover simple to complex use cases for you. Here are some examples to show you how does it work.
⚠️ Most modern browsers support ResizeObserver natively. You can also use polyfill for full browser support.
Basic Use Case
To report the size of an element by the width
and height
states.
import useDimensions from "react-cool-dimensions";
const App = () => {
const { observe, unobserve, width, height, entry } = useDimensions({
onResize: ({ observe, unobserve, width, height, entry }) => {
unobserve();
observe();
},
});
return (
<div ref={observe}>
Hi! My width is {width}px and height is {height}px
</div>
);
};
💡 You don't have to call unobserve
when the component is unmounted, this hook will handle it for you.
Responsive Components
We have media queries but those are based on the browser viewport not individual elements. In some cases, we'd like to style components based on the width of a containing element rather than the browser viewport. To meet this demand there's a proposal for container queries, but it still doesn't exist today...
No worries, react-cool-dimensions
provides an alternative solution for us! We can activate the responsive mode by the breakpoints
option. It's a width-based solution, once it's activated we can easily apply different styles to a component according to the currentBreakpoint
state. The overall concept as below.
If you wish to update the state on the breakpoints changed, you can set the updateOnBreakpointChange
option to true
.
import useDimensions from "react-cool-dimensions";
const Card = () => {
const { observe, currentBreakpoint } = useDimensions({
breakpoints: { XS: 0, SM: 320, MD: 480, LG: 640 },
updateOnBreakpointChange: true,
onResize: ({ currentBreakpoint }) => {
},
});
return (
<div class={`card ${currentBreakpoint}`} ref={observe}>
<div class="card-header">I'm 😎</div>
<div class="card-body">I'm 👕</div>
<div class="card-footer">I'm 👟</div>
</div>
);
};
Note: If the breakpoints
option isn't set or there's no the defined breakpoint (object key) for a range of width. The currentBreakpoint
will be empty string.
Conditionally Updating State
You can use the shouldUpdate
option to conditionally update the state to reduce unnecessary re-renders as below.
const returnObj = useDimensions({
shouldUpdate: ({ currentBreakpoint, width, height, entry }) => {
return state.width > 300;
},
});
Note: When updateOnBreakpointChange
and shouldUpdate
are used at the same time, shouldUpdate
has a higher priority.
Border-box Size Measurement
By default, the hook reports the width
and height
based on the content rectangle of the target element. We can include the padding and border for measuring by the useBorderBoxSize
option. Please note, the width
and height
states are rely on the ResizeObserverEntry.borderBoxSize but it hasn't widely implemented by browsers therefore we need to use polyfill for this feature.
import useDimensions from "react-cool-dimensions";
import { ResizeObserver } from "@juggle/resize-observer";
const App = () => {
const { observe, width, height } = useDimensions({
useBorderBoxSize: true,
polyfill: ResizeObserver,
});
return (
<div
style={{
width: "100px",
height: "100px",
padding: "10px",
border: "5px solid grey",
}}
ref={observe}
>
{/* Now the width and height will be: 100px + 10px + 5px = 115px */}
Hi! My width is {width}px and height is {height}px
</div>
);
};
How to Share A ref
?
You can share a ref
as follows:
import { useRef } from "react";
import useDimensions from "react-cool-dimensions";
const App = () => {
const ref = useRef();
const { observe } = useDimensions();
return (
<div
ref={(el) => {
observe(el); // Set the target element for measuring
ref.current = el; // Share the element for other purposes
}}
/>
);
};
Performance Optimization
The onResize
event will be triggered whenever the size of the target element is changed. We can reduce the frequency of the event callback by activating the responsive mode or implementing our own throttled/debounced function as below. Note that in order to throttle/debounce the function correctly, it will need to be memorized else it will be recreated on every render call.
import { useMemo } from "react";
import _ from "lodash";
const returnObj = useDimensions({
onResize: useMemo(
() =>
_.throttle(() => {
}, 500),
[]
),
});
Working in TypeScript
This hook supports TypeScript, you can tell the hook what type of element you are going to observe through the generic type:
const App = () => {
const { observe } = useDimensions<HTMLDivElement>();
return <div ref={observe} />;
};
💡 For more available types, please check it out.
API
const returnObj = useDimensions(options?: object);
Return object
It's returned with the following properties.
Key | Type | Default | Description |
---|
observe | function | | To set a target element for measuring or re-start observing the current target element. |
unobserve | function | | To stop observing the current target element. |
width | number or null | null | The width of the target element in pixel. Null while target has not mounted. |
height | number or null | null | The height of the target element in pixel. Null while target has not mounted.Z |
currentBreakpoint | string | | Indicates the current breakpoint of the responsive components. |
entry | object | | The ResizeObserverEntry of the target element. |
Parameter
The options
provides the following configurations and event callback for you.
ResizeObserver Polyfill
ResizeObserver has good support amongst browsers, but it's not universal. You'll need to use polyfill for browsers that don't support it. Polyfills is something you should do consciously at the application level. Therefore react-cool-dimensions
doesn't include it.
We recommend using @juggle/resize-observer:
$ yarn add @juggle/resize-observer
$ npm install --save @juggle/resize-observer
Then inject it by the polyfill
option:
import { ResizeObserver } from "@juggle/resize-observer";
const { width, height } = useDimensions(ref, { polyfill: ResizeObserver });
Or pollute the window
object:
import { ResizeObserver, ResizeObserverEntry } from "@juggle/resize-observer";
if (!("ResizeObserver" in window)) {
window.ResizeObserver = ResizeObserver;
}
You could use dynamic imports to only load the file when the polyfill is required:
(async () => {
if (!("ResizeObserver" in window)) {
const module = await import("@juggle/resize-observer");
window.ResizeObserver = module.ResizeObserver;
}
})();
Articles / Blog Posts
💡 If you have written any blog post or article about react-cool-dimensions
, please open a PR to add it here.
Contributors ✨
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!