Skip to content

Repository files navigation

@blocz/react-responsive

@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.

See changelog

Table of contents

  1. Installation
  2. How to use
    1. useMediaQuery()
    2. Media ranges
      1. Default media ranges
      2. Additional Up and Down
      3. useMediaRange()
      4. <Only>
        1. on prop
        2. matchMedia
        3. Render as component (deprecated)
      5. Custom media ranges: createMediaRanges()
        1. Strictly typed
        2. Stricter <Only>
        3. Units & direction
      6. <MediaRangesProvider> (deprecated)
        1. Add more media ranges
        2. Change default media ranges
        3. Units
        4. Direction
  3. Comparison to other libraries
  4. matchMedia polyfill
    1. Browser
    2. Node
  5. React 16 / 17 support
  6. Deprecated APIs
  7. FAQ

Installation

# pnpm
pnpm add @blocz/react-responsive
# yarn
yarn add @blocz/react-responsive
# npm
npm install @blocz/react-responsive

How to use

useMediaQuery()

useMediaQuery() is a hook that detects if the given media query matches the current viewport.

importReactfrom"react";import{useMediaQuery}from"@blocz/react-responsive";constApp=()=>{constisLandscape=useMediaQuery("(orientation: landscape)");return<p>{isLandscape ? "Landscape mode" : "Portrait mode"}</p>;};

Learn more about CSS media queries

Media ranges

@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.

Default media ranges

By default, the media ranges are:

Media rangeFromTo
xs0px575px
sm576px767px
md768px991px
lg992px1199px
xl1200pxInfinity

This makes it fully explicit: a lg device is not md nor xl.

Additional Up and Down

Each media range also comes with {mediaRange}Up and {mediaRange}Down variants – covering everything above or below that breakpoint:

Media rangeFromTo
xsUp0pxInfinity
smUp576pxInfinity
mdUp768pxInfinity
lgUp992pxInfinity
xlUp1200pxInfinity
Media rangeFromTo
xsDown0px575px
smDown0px767px
mdDown0px991px
lgDown0px1199px
xlDown0pxInfinity

Note:xsDown is equivalent to xs, xlUp is equivalent to xl, and xlDown/xsUp match all screen sizes – these exist only for convenience.

useMediaRange()

useMediaRange() is a hook that detects if the given media range matches the current viewport.

importReactfrom"react";import{useMediaRange}from"@blocz/react-responsive";constApp=()=>{constmatchXl=useMediaRange("xl");constmatchMdDown=useMediaRange("mdDown");constmatchMdOrLg=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>

<Only> is the component equivalent of useMediaRange() and useMediaQuery(): it renders its children only when the condition matches.

on prop

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.

importReactfrom"react";import{Only}from"@blocz/react-responsive";constApp=()=>(<React.Fragment><Onlyon="xs">Only visible for extra small devices (portrait phones)</Only><Onlyon="sm">Only visible for small devices (landscape phones)</Only><Onlyon="md">Only visible for medium devices (tablets)</Only><Onlyon="lg">Only visible for large devices (desktops)</Only><Onlyon="xl">Only visible for extra large devices (large desktops)</Only><Onlyon="sm xl">Only visible for small AND extra large devices</Only></React.Fragment>);
matchMedia

The matchMedia prop behaves like useMediaQuery(): it accepts any regular query supported by window.matchMedia.

importReactfrom"react";import{Only}from"@blocz/react-responsive";constApp=()=>(<OnlymatchMedia="(min-device-width: 500px) and (orientation: landscape)">
Visible on every device bigger than "500px" and in landscape mode
</Only>);

Note: If you use on AND matchMedia together, the component renders if any of the media ranges matches OR if the media query is fulfilled (not AND).

Render as component (deprecated)

⚠️ Using the as prop 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:

importReactfrom"react";import{Only}from"@blocz/react-responsive";constApp=()=>(<ul><Onlyas="li"on="xs">
Only visible for extra small devices
</Only><Onlyas="li"on="sm">
Only visible for small devices
</Only><Onlyas="li"on="md">
Only visible for medium devices
</Only></ul>);

The as prop also accepts React components:

importReactfrom"react";import{Only}from"@blocz/react-responsive";constCustom=({ title, children })=>(<React.Fragment><h3>{title}</h3><p>{children}</p></React.Fragment>);constApp=()=>(<React.Fragment><Onlyas={Custom}title="xs"on="xs">
Only visible for extra small devices
</Only><Onlyas={Custom}title="sm"on="sm">
Only visible for small devices
</Only><Onlyas={Custom}title="md"on="md">
Only visible for medium devices
</Only></React.Fragment>);

Custom media ranges: createMediaRanges()

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.

Strictly typed

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 erroruseMediaRange("md invalid");// ❌ TS error – "md" is fine, "invalid" is not

This is also true for the returned <Only>:

<><Only// ✅on="md pxRange"></Only><Only// ❌ TS erroron="lg invalid"></Only></>
Stricter <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).

Units & direction

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> (deprecated)

⚠️<MediaRangesProvider> is deprecated and will be removed in v6.0.0. Use createMediaRanges() instead.

<MediaRangesProvider> defines all media range values.

Use it to inject or modify the media ranges (only use one <MediaRangesProvider> per build).

Add more media ranges
importReactfrom"react";import{Only,MediaRangesProvider}from"@blocz/react-responsive";constApp=()=>(<MediaRangesProvideradditionalMediaRanges={{customRange: [263,863]}}><Onlyon="customRange">Visible on every device from "263px" to "863px"</Only><Onlyon="customRangeUp">Visible on every device bigger than "263px"</Only><Onlyon="customRangeDown">Visible on every device smaller than "863px"</Only></MediaRangesProvider>);
Change default media ranges
importReactfrom"react";import{Only,MediaRangesProvider}from"@blocz/react-responsive";constApp=()=>(<MediaRangesProvidermediaRanges={{sm: [263,863]}}><Onlyon="sm">Visible on every device from "263px" to "863px"</Only><Onlyon="smUp">Visible on every device bigger than "263px"</Only><Onlyon="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 xlare no longer defined!

Units

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.

importReactfrom"react";import{Only,MediaRangesProvider}from"@blocz/react-responsive";constApp=()=>(<MediaRangesProvideradditionalMediaRanges={{pxRange: [263,863,{unit: "px"}],emRange: [20,40,{unit: "em"}],}}><Onlyon="pxRange">Visible on every device from "263px" to "863px"</Only><Onlyon="emRange">Visible on every device from "20em" to "40em"</Only></MediaRangesProvider>);
Direction

You can specify which direction is used for the media queries (height or width).

By default, "width" is the chosen direction.

importReactfrom"react";import{Only,MediaRangesProvider}from"@blocz/react-responsive";constApp=()=>(<MediaRangesProvidermediaRanges={{xRange: [300,500,{direction: "width"}],yRange: [200,400,{direction: "height"}],}}><Onlyon="xRange">Visible on every device from "300px" to "500px" wide</Only><Onlyon="yRange">Visible on every device from "200px" to "400px" tall</Only></MediaRangesProvider>);

Comparison to other libraries

LibMedia rangesCustom media rangesMedia querymatchMedia listener*hooksSSR 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).

matchMedia polyfill

Browser

If you want to use matchMedia in browsers that don't support it, I'd recommend matchmedia-polyfill.

Node

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.

React 16 / 17 support

@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.

Deprecated APIs

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:

DeprecatedReplacement
useBreakpoint()useMediaRange()
<BreakpointsProvider><MediaRangesProvider>
<BreakpointsContext><MediaRangesContext>
<MediaRangesProvider>createMediaRanges()
<MediaRangesContext>createMediaRanges()

FAQ

For other questions, please take a look at our FAQ document.

About

🔍 <Only /> displays some contents for particular screen sizes

Topics

Resources

Stars

24 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages