It is sometimes helpful to know the responsive breakpoints in JavaScript to tweak and run the logic depending on the screen size.
We will be creating a Custom React Hook. It will determine the screen size. And, we will be able to use the screen sizes just like we do in CSS media queries to update the UI or make the logic run a specific way.
The code is dependent on the
window
object of the browser. The solution won't work in case of server-side rendering where thewindow
object won't exist.
We will name the custom hook useWindowSize
. We will have a state variable called windowSize
which will be exported to be used by React Components.
import { useState, useEffect } from "react";
function useWindowSize() {
const [windowSize, setWindowSize] = useState(undefined);
...
//code to determine the screen size will go here
...
//expose windowSize variable to be used by Components
// to make responsiveness related chanegs
return windowSize;
}
export default useWindowSize;
Now to determine the screen size, we will first check if the client is a browser, by checking if we have access to window
object. If we have, we can get the width of the screen using window.innerWidth
and assign into the state variable as default value.
import { useState, useEffect } from "react";
function useWindowSize() {
//👇
const isWindowClient = typeof window === "object";
const [windowSize, setWindowSize] = useState(
isWindowClient ? window.innerWidth : undefined
);
//☝️
return windowSize;
}
export default useWindowSize;
You can use this hook in the component as below,
import React from "react";
import useWindowSize from "./useWindowSize";
export default function App() {
const windowSize = useWindowSize();
return (
<div>
<h1>The screen width is: </h1>
<span style={{ fontSize: "30px" }}>{windowSize}</span>
</div>
);
}
However, on resize of the window, this hook won't inform us about the change in size(as shown above gif). To achieve it, we will have to implement window
's on resize
listener. We will use it in useEffect
so that we won't register the listener each time it renders and we make sure that it gets unregistered when it needs to.
import { useState, useEffect } from "react";
function useWindowSize() {
const isWindowClient = typeof window === "object";
const [windowSize, setWindowSize] = useState(
isWindowClient ? window.innerWidth : undefined
);
//👇
useEffect(() => {
//a handler which will be called on change of the screen resize
function setSize() {
setWindowSize(window.innerWidth);
}
if (isWindowClient) {
//register the window resize listener
window.addEventListener("resize", setSize);
//un-register the listener
return () => window.removeEventListener("resize", setSize);
}
}, [isWindowClient, setWindowSize]);
//☝️
return windowSize;
}
export default useWindowSize;
Now if we want to have breakpoints instead of this absolute widths, we can convert the sizes into specific breakpoints (let say sm
, md
, lg
, xlg
) using a simple util function.
import { useState, useEffect } from "react";
//👇
//a Util function that will conver the absolute width into breakpoints
function getBreakPoint(windowWidth) {
if (windowWidth) {
if (windowWidth < 480) {
return "sm";
} else if (windowWidth < 1024) {
return "md";
} else if (windowWidth < 1200) {
return "lg";
} else {
return "xlg";
}
} else {
return undefined;
}
}
//☝️
function useWindowSize() {
const isWindowClient = typeof window === "object";
const [windowSize, setWindowSize] = useState(
isWindowClient
? getBreakPoint(window.innerWidth) //👈
: undefined
);
useEffect(() => {
//a handler which will be called on change of the screen resize
function setSize() {
setWindowSize(getBreakPoint(window.innerWidth)); //👈
}
if (isWindowClient) {
//register the window resize listener
window.addEventListener("resize", setSize);
//unregister the listerner on destroy of the hook
return () => window.removeEventListener("resize", setSize);
}
}, [isWindowClient, setWindowSize]);
return windowSize;
}
export default useWindowSize;
The codesandbox of the final code is below
All the best for making your users happy with all size of devices! 😄
Cover Photo by Hal Gatewood on Unsplash
Top comments (12)
I would prefer making this more general by taking a configuration object with the desired sizes:
Cool! Additionally, the way you used
Object.values + find + map
, refreshed usage of few concepts which I knew but rarely put in practice. Thanks.My aproach is this way: (Someday I'll publish my hooks xD)
So, you can use like this:
Wow this is an comprehensive one. I think I should sit back and give time to imagine all use cases it covers.
Hi,
I am just wondering why you use matchMedia instead of all these complex calculations? media queries in JavaScript too matchMedia
Thanks
Stefan
First time heard about the matchMedia. Thanks for the input.
Just wondering, is there any way to react if screen resized?
Of course, it has an event receiver for onChange.
developer.mozilla.org/en-US/docs/W...
that's cool!
Hello,
I am new to React and have never used a custom houk. It seems to me that I understood how your code works, but I don't quite understand how to build the logic on a component in order to return inside the choice of 2 components like the navigation bar. Ex: I have a global topBar container, and I have 2 components to return, either a dropdownMenu or from the large tablet, bring up the other navigation component navMenu.
How can I implement the logic against the code you offer?
Thank you.
Thank you very much. I was reading this article and blessing you from Perú, keep going :)
Thanks for your kind words, Favio😊
This does not work on page reload on mobile devices or when you go from one page to another page in mobile device. It returns infinity value in that case.