---
title: "How to Build Custom React Media Query Hook for Responsive Apps"
description: "Learn how to make your React, Gatsby, and Next.js applications more responsive with a touch of JS and combining some cool APIs. Media queries to the rescue!"
source: "https://www.netlify.com/blog/2020/12/05/building-a-custom-react-media-query-hook-for-more-responsive-apps/"
last_updated: "2026-07-15T19:52:36.000Z"
---
Welcome to Blogvent, day 5!

Chances are if you’ve written any CSS before, you’ve written media queries. And honestly, media queries overall are solid! But, they were made for an earlier time in the browser. They were not designed for some of the rendering logic that we have on the front-end now.

You can still use media queries, of course, and should, but there are some cases where JavaScript will be a smarter option. For example, what if you’re on your phone, and browsing a website, and there is a sidebar or element that is hidden by CSS, that is making network requests? For the user, that is a waste of resources!

There has to be a better way. And there is!

## Media queries… in JavaScript!

So, to solve this problem, what you need to do here is conditionally render things based on the browser size, rather than render something and hide it with CSS.

If you’ll recall in [yesterday’s Blogvent post](https://www.netlify.com/blog/2020/12/04/escaping-next.js-to-access-the-browser/), you can use React’s `useEffect` to access the `window` object in the browser. That `window` object has a function called `matchMedia` that returns a boolean based on if the window matches a certain media query passed in!

So, if we combine these with a little bit of state, you can make a custom hook that you can use to conditionally render components in your applications:

```
import { useState, useEffect } from 'react';
export function useMediaQuery(query) {  const [matches, setMatches] = useState(false);
  useEffect(() => {    const media = window.matchMedia(query);    if (media.matches !== matches) {      setMatches(media.matches);    }    const listener = () => {      setMatches(media.matches);    };    media.addListener(listener);    return () => media.removeListener(listener);  }, [matches, query]);
  return matches;}
```

Let’s walk through this. In this custom hook, you have a `matches` state variable, and we take in a `query`. In the effect, we check if the `query` that is passed in matches the window. If it does, we set `matches` to true. We set an event listener in there as well, to keep that variable in sync with the window changing sizes. The event listener is removed when `query` changes, when the component using it unmount, or when `matches` changes.

## Whoa. How can I see this in action?

Feel free to use this hook in your projects! You can call it inside your components, for example:

```
function Page() {  let isPageWide = useMediaQuery('(min-width: 800px)')
    return <>    {isPageWide && <UnnecessarySidebar />}    <ImportantContent />  </>}
```

If you’d like to see it in action in a real project, check out the [Jamstack Explorers repo](https://www.youtube.com/c/NetlifyApp/playlists) and how we render [our Navigation component](https://github.com/netlify/explorers/blob/main/src/components/Navigation.js#L14).

And, if you’d like to learn more about Next.js, check out the course (with more to come) on [Jamstack Explorers](https://www.youtube.com/watch?v=0qXjtznmhDI&list=PLzlG0L9jlhENGgDUr09a7JdRgSTybmE1P)!

### Share

-   [X (fka Twitter)](https://twitter.com/intent/tweet?text=Building a custom React media query hook for more responsive apps&url=https://www.netlify.com/blog/2020/12/05/building-a-custom-react-media-query-hook-for-more-responsive-apps//)
-   [LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.netlify.com%2Fblog%2F2020%2F12%2F05%2Fbuilding-a-custom-react-media-query-hook-for-more-responsive-apps%2F%2F)
-   [Facebook](https://www.facebook.com/sharer.php?u=https://www.netlify.com/blog/2020/12/05/building-a-custom-react-media-query-hook-for-more-responsive-apps//)
-   [Bluesky](https://bsky.app/intent/compose?text=Building a custom React media query hook for more responsive apps+https://www.netlify.com/blog/2020/12/05/building-a-custom-react-media-query-hook-for-more-responsive-apps//)

* * *

### Tags

-   [Next.js](/blog/tags/next.js/)
-   [blogvent](/blog/tags/blogvent/)

## Keep reading

![](/images/blog-fallback-thumbnail.svg)

Guides & Tutorials December 4, 2020

[

### “Escaping” Next.js to access the browser

](/blog/2020/12/04/escaping-next.js-to-access-the-browser/)

-   ![Profile picture of Cassidy Williams](/_astro/a62099fab0f946e063c4b84ff4b4d9c94f9aa7a5-400x400_ZdakPa.webp)
    
    Cassidy Williams
    

![](/images/blog-fallback-thumbnail.svg)

Guides & Tutorials December 2, 2020

[

### Next.js: Should I use SSR or SSG?

](/blog/2020/12/02/next.js-should-i-use-ssr-or-ssg/)

-   ![Profile picture of Cassidy Williams](/_astro/a62099fab0f946e063c4b84ff4b4d9c94f9aa7a5-400x400_ZdakPa.webp)
    
    Cassidy Williams
    

## Recent posts

News & Announcements July 14, 2026

[

### More headroom, a lower per-credit rate, and a bill you can predict: introducing new Pro plan tiers

](/blog/new-pro-plan-tiers)

-   ![Profile picture of Gehrig Kunz](/_astro/b4e9f58d914d1334ea70d53ea55a1f26b26f1445-512x512_17SwOI.webp)
    
    Gehrig Kunz
    

News & Announcements July 13, 2026

[

### Netlify inside of Claude Design

](/blog/netlify-inside-of-claude-design)

-   ![Profile picture of Sean Roberts](/_astro/bbf2243f8171dbddd80ab2103622106cef84d125-512x512_Z1d2LKE.webp)
    
    Sean Roberts
    

News & Announcements June 25, 2026

[

### Netlify Functions, designed for Agent Experience

](/blog/netlify-functions-designed-for-agent-experience)

-   ![Profile picture of Eduardo Bouças](/_astro/52958f21e8450baf6d8e60302341a984e220c0cd-512x512_13VDlu.webp)
    
    Eduardo Bouças
    

![](/_astro/3f255b372fa958df35802666ee33b4609b2d71bd-1200x1586_1VtE2D.webp)

### How do the best dev and marketing teams work together?

[Access the report](https://www.netlify.com/reports/2024-leadership-trend-report/access/)