@blocz/react-responsive is inspired by the .visible classes from bootstrap 4 (and .hidden classes from bootstrap 3): it lets you show or hide components based on the current screen size.
- Installation
- How to use
- Comparison to other libraries
matchMediapolyfill- React 16 / 17 support
- Deprecated APIs
- FAQ
# pnpm
pnpm add @blocz/react-responsive
# yarn
yarn add @blocz/react-responsive
# npm
npm install @blocz/react-responsiveuseMediaQuery() is a hook that detects if the given media query matches the current viewport.
import React from "react";
import { useMediaQuery } from "@blocz/react-responsive";
const App = () => {
const isLandscape = useMediaQuery("(orientation: landscape)");
return <p>{isLandscape ? "Landscape mode" : "Portrait mode"}</p>;
};Learn more about CSS media queries
@blocz/react-responsive is based on the classic bootstrap breakpoints: xs, sm, md, lg and xl.
Unlike Bootstrap β where xs, sm, etc. are single breakpoints β @blocz/react-responsive uses media ranges: each name describes the interval between two breakpoints, making ranges explicit and non-overlapping.
See Why media ranges instead of breakpoints? for more context.
By default, the media ranges are:
| Media range | From | To |
|---|---|---|
xs |
0px | 575px |
sm |
576px | 767px |
md |
768px | 991px |
lg |
992px | 1199px |
xl |
1200px | Infinity |
This makes it fully explicit: a lg device is not md nor xl.
Each media range also comes with {mediaRange}Up and {mediaRange}Down variants β covering everything above or below that breakpoint:
| Media range | From | To |
|---|---|---|
xsUp |
0px | Infinity |
smUp |
576px | Infinity |
mdUp |
768px | Infinity |
lgUp |
992px | Infinity |
xlUp |
1200px | Infinity |
| Media range | From | To |
|---|---|---|
xsDown |
0px | 575px |
smDown |
0px | 767px |
mdDown |
0px | 991px |
lgDown |
0px | 1199px |
xlDown |
0px | Infinity |
Note:
xsDownis equivalent toxs,xlUpis equivalent toxl, andxlDown/xsUpmatch all screen sizes β these exist only for convenience.
useMediaRange() is a hook that detects if the given media range matches the current viewport.
import React from "react";
import { useMediaRange } from "@blocz/react-responsive";
const App = () => {
const matchXl = useMediaRange("xl");
const matchMdDown = useMediaRange("mdDown");
const matchMdOrLg = useMediaRange("md lg");
return (
<ul>
{matchXl && <li>Visible on every "large" device</li>}
{matchMdDown && <li>Visible on every device smaller than or equal to "medium"</li>}
{matchMdOrLg && <li>Visible on every "medium" or "large" device</li>}
</ul>
);
};<Only> is the component equivalent of useMediaRange() and useMediaQuery(): it renders its children only when the condition matches.
The on prop behaves like useMediaRange(): it accepts a media range name (or a space-separated list of names) and makes <Only> render its children when any of the named ranges match.
import React from "react";
import { Only } from "@blocz/react-responsive";
const App = () => (
<React.Fragment>
<Only on="xs">Only visible for extra small devices (portrait phones)</Only>
<Only on="sm">Only visible for small devices (landscape phones)</Only>
<Only on="md">Only visible for medium devices (tablets)</Only>
<Only on="lg">Only visible for large devices (desktops)</Only>
<Only on="xl">Only visible for extra large devices (large desktops)</Only>
<Only on="sm xl">Only visible for small AND extra large devices</Only>
</React.Fragment>
);The matchMedia prop behaves like useMediaQuery(): it accepts any regular query supported by window.matchMedia.
import React from "react";
import { Only } from "@blocz/react-responsive";
const App = () => (
<Only matchMedia="(min-device-width: 500px) and (orientation: landscape)">
Visible on every device bigger than "500px" and in landscape mode
</Only>
);Note: If you use
onANDmatchMediatogether, the component renders if any of the media ranges matches OR if the media query is fulfilled (not AND).
β οΈ Using theasprop on<Only>is deprecated and will be removed in v6.0.0. This is not considered as type-safe
The as prop makes <Only> render as a different element (any DOM tag or React component). Any props except on, matchMedia, and as are forwarded to it:
import React from "react";
import { Only } from "@blocz/react-responsive";
const App = () => (
<ul>
<Only as="li" on="xs">
Only visible for extra small devices
</Only>
<Only as="li" on="sm">
Only visible for small devices
</Only>
<Only as="li" on="md">
Only visible for medium devices
</Only>
</ul>
);The as prop also accepts React components:
import React from "react";
import { Only } from "@blocz/react-responsive";
const Custom = ({ title, children }) => (
<React.Fragment>
<h3>{title}</h3>
<p>{children}</p>
</React.Fragment>
);
const App = () => (
<React.Fragment>
<Only as={Custom} title="xs" on="xs">
Only visible for extra small devices
</Only>
<Only as={Custom} title="sm" on="sm">
Only visible for small devices
</Only>
<Only as={Custom} title="md" on="md">
Only visible for medium devices
</Only>
</React.Fragment>
);createMediaRanges() is the recommended way to customize the media ranges. It returns an object { useMediaRange, Only } bound to the ranges you pass in, with end-to-end TypeScript types.
import { createMediaRanges, DEFAULT_MEDIA_RANGES } from "@blocz/react-responsive";
const { useMediaRange, Only } = createMediaRanges({
...DEFAULT_MEDIA_RANGES,
pxRange: [263, 863, { unit: "px" }],
emRange: [20, 40, { unit: "em" }],
});If you want to re-use the same defaults as the top-level <Only> & useMediaRange(), you'll need to import & use DEFAULT_MEDIA_RANGES.
The returned useMediaRange() accepts only the names that match the ranges you declared (plus the auto-generated Up and Down aliases). The passed string can hold a single name or a space-separated list, every media range will be typechecked:
useMediaRange("md"); // β
useMediaRange("pxRangeUp"); // β
useMediaRange("mdDown"); // β
useMediaRange("md pxRange"); // β
useMediaRange("invalid"); // β TS error
useMediaRange("md invalid"); // β TS error β "md" is fine, "invalid" is notThis is also true for the returned <Only>:
<>
<Only
// β
on="md pxRange"
>
β¦
</Only>
<Only
// β TS error
on="lg invalid"
>
β¦
</Only>
</>Unlike the top-level <Only>, the <Only> returned from createMediaRanges() does not support the as prop (and does not forward additional props to an inner element).
Each range entry accepts one of these shapes: [min, max], or [min, max, { unit?, direction? }]:
const { Only } = createMediaRanges({
pxRange: [263, 863, { unit: "px" }],
emRange: [20, 40, { unit: "em" }],
yRange: [200, 400, { direction: "height" }],
});
β οΈ <MediaRangesProvider>is deprecated and will be removed in v6.0.0. UsecreateMediaRanges()instead.
<MediaRangesProvider> defines all media range values.
Use it to inject or modify the media ranges (only use one <MediaRangesProvider> per build).
import React from "react";
import { Only, MediaRangesProvider } from "@blocz/react-responsive";
const App = () => (
<MediaRangesProvider additionalMediaRanges={{ customRange: [263, 863] }}>
<Only on="customRange">Visible on every device from "263px" to "863px"</Only>
<Only on="customRangeUp">Visible on every device bigger than "263px"</Only>
<Only on="customRangeDown">Visible on every device smaller than "863px"</Only>
</MediaRangesProvider>
);import React from "react";
import { Only, MediaRangesProvider } from "@blocz/react-responsive";
const App = () => (
<MediaRangesProvider mediaRanges={{ sm: [263, 863] }}>
<Only on="sm">Visible on every device from "263px" to "863px"</Only>
<Only on="smUp">Visible on every device bigger than "263px"</Only>
<Only on="smDown">Visible on every device smaller than "863px"</Only>
</MediaRangesProvider>
);Warning: This overrides completely the default media ranges, in this example, the other media ranges xs, md, lg and xl are no longer defined!
You can specify which unit is going to be used for the media range by specifying in the 3rd option a "unit" key.
Every CSS unit is supported. The default unit is px.
import React from "react";
import { Only, MediaRangesProvider } from "@blocz/react-responsive";
const App = () => (
<MediaRangesProvider
additionalMediaRanges={{
pxRange: [263, 863, { unit: "px" }],
emRange: [20, 40, { unit: "em" }],
}}
>
<Only on="pxRange">Visible on every device from "263px" to "863px"</Only>
<Only on="emRange">Visible on every device from "20em" to "40em"</Only>
</MediaRangesProvider>
);You can specify which direction is used for the media queries (height or width).
By default, "width" is the chosen direction.
import React from "react";
import { Only, MediaRangesProvider } from "@blocz/react-responsive";
const App = () => (
<MediaRangesProvider
mediaRanges={{
xRange: [300, 500, { direction: "width" }],
yRange: [200, 400, { direction: "height" }],
}}
>
<Only on="xRange">Visible on every device from "300px" to "500px" wide</Only>
<Only on="yRange">Visible on every device from "200px" to "400px" tall</Only>
</MediaRangesProvider>
);| Lib | Media ranges | Custom media ranges | Media query | matchMedia listener* |
hooks | SSR support |
|---|---|---|---|---|---|---|
| @blocz/react-responsive | β | β | β | β | β | β |
| react-responsive | β | β | β | β | β | β |
| react-breakpoints | β | β | β | β | β | β |
| react-responsive-breakpoints | β | β | β | β | β | β |
*: matchMedia listener event means that the library is built around matchMedia.addListener(callback) and not window.addEventListener('resize', callback) (which is faster because the callback is only triggered when the media query's state changes and not at every resize).
If you want to use matchMedia in browsers that don't support it, I'd recommend matchmedia-polyfill.
If you want to mock matchMedia on Node to execute tests for instance, you can use mock-match-media.
And if you need an example with Jest, @testing-library/react, React and @blocz/react-responsive, you can take a look at these tests.
@blocz/react-responsive relies on useSyncExternalStore. This function was added in React 18.
If you are on React 16.8+ / React 17, you'll need to use use-sync-external-store to polyfill useSyncExternalStore.
The terminology used by this library used to be "breakpoint". It was renamed to "media range" because each entry actually describes the range between two breakpoints rather than a single breakpoint.
For backward compatibility, the previous exports are still available but marked as @deprecated, and will be removed in the next major release:
| Deprecated | Replacement |
|---|---|
useBreakpoint() |
useMediaRange() |
<BreakpointsProvider> |
<MediaRangesProvider> |
<BreakpointsContext> |
<MediaRangesContext> |
breakpoints prop |
mediaRanges prop |
additionalBreakpoints prop |
additionalMediaRanges prop |
<MediaRangesProvider> |
createMediaRanges() |
<MediaRangesContext> |
createMediaRanges() |
For other questions, please take a look at our FAQ document.