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)
)
}, [])
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
Add the provider
import {
ResponsivenessProvider,
Presets,
} from "react-responsiveness";
function App() {
// ...
}
const WithResponsiveness = () => (
<ResponsivenessProvider breakpoints={Presets.Tailwind_CSS}>
<App />
</ResponsivenessProvider>
);
export default WithResponsiveness;
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>
)}
</>
)
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>
{isOnly('medium') && (
// @media (min-width: 777px) and (max-width: 1233.9px)
<div>content...</div>
)}
Demo
If you find it useful, let me know.
Cheers!
Top comments (0)