React. Responsiveness made easy

andreigheorghiu

Andrei Gheorghiu

Posted on March 7, 2023

React. Responsiveness made easy

The problem

Sometimes you want the current responsiveness interval in JavaScript.

Most attempts (and answers on StackOverflow) are placing an event listener on window.resize and then use some custom logic (a switch statement) to output the current interval.

Works, but not ideal.

Biggest problem is it runs on window.resize, which fires tens of times per second while resizing, creating tiny freezes in page display, a clear sign the browser is struggling


The solution

A cleaner (and more performant) alternative is to use window.matchMedia(queryString).

It returns an instance of MediaQueryList.

Binding a callback on this instance's change event ensures the callback will only be called when the media query starts/stops matching.

const [isSm, setIsSm] = useState(false)
useEffect(() => {
  const queryList = window.matchMedia(
    '(min-width: 640px) and (max-width: 767.9px)'
  )
  queryList.addEventListener('change', ({ matches }) =>
    setIsSm(matches)
  )
}, [])
Enter fullscreen mode Exit fullscreen mode

The other cool thing about it is you don't need to clean up the bound event. If you delete the MediaQueryList itself, the listener is garbage collected, so no listeners are left behind on <body> or window object after the component unmounted.


Wrap up

Having seen this question answered less than ideal a few times, I decided to write a tiny plugin (react-responsiveness) which takes an optional config object (interval: min dictionary - defaults to Bootstrap 5's breakpoints) and returns a context provider and a hook.

I added presets for commonly used frameworks: Bootstrap_3, Bootstrap_4, Bootstrap_5, Bulma, Chakra, Foundation, Ionic, Material_Design, Materialize, Material_UI, Quasar, Semantic_UI, Skeleton, Tailwind_CSS, Vuetify, Windi_CSS.


Installation

yarn add react-responsiveness
Enter fullscreen mode Exit fullscreen mode

Add the provider

import {
  ResponsivenessProvider,
  Presets,
} from "react-responsiveness";

function App() {
  // ...
}

const WithResponsiveness = () => (
  <ResponsivenessProvider breakpoints={Presets.Tailwind_CSS}>
    <App />
  </ResponsivenessProvider>
);
export default WithResponsiveness;
Enter fullscreen mode Exit fullscreen mode

Use in components

The hook exposes isMin, isMax and isOnly helper methods, the currentInterval and a more verbose matches object, which can be all be used to show/hide things responsively:

import { useResponsiveness } from "react-responsiveness"

const { isMin, isMax, isOnly, currentInterval } =
  useResponsiveness()

return (
  <>
    <div>Current interval {currentInterval}</div>
    {isMin("md") && (
      // @media (min-width: 768px)
      <div>content...</div>
    )}
    {isMax("md") && (
      // @media (max-width: 991.9px)
      <div>content...</div>
    )}
    {isOnly("md") && (
      // @media (min-width: 768px) and (max-width: 991.9px)
      <div>content...</div>
    )}
  </>
)
Enter fullscreen mode Exit fullscreen mode

Note: the interval names vary based on the preset or config you used.


Use with bespoke breakpoints

<ResponsivenessProvider
  breakpoints={{
    small: 0,
    medium: 777,
    large: 1234,
  }}
>
  // ...
</ResponsivenessProvider>
Enter fullscreen mode Exit fullscreen mode
{isOnly('medium') && (
  // @media (min-width: 777px) and (max-width: 1233.9px)
  <div>content...</div>
)}
Enter fullscreen mode Exit fullscreen mode

Demo

https://codesandbox.io/p/github/codesandbox/codesandbox-template-vite-react/csb-ss87sf/react-responsiveness-v1?file=%2Fsrc%2FApp.tsx


If you find it useful, let me know.

Cheers!

💖 💪 🙅 🚩
andreigheorghiu
Andrei Gheorghiu

Posted on March 7, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related