useMediaQuery
This React hook listens for matches to a CSS media query. It allows the rendering of components based on whether the query matches or not.
Some of the key features:
- ⚛️ It has an idiomatic React API.
- 🚀 It's performant, it observes the document to detect when its media queries change, instead of polling the values periodically.
- 📦1.1 kB gzipped.
- 🤖 It supports server-side rendering.
Basic media query
You should provide a media query to the first argument of the hook.The media query string can be any valid CSS media query, for example'(prefers-color-scheme: dark)'.
Using the query'print' to modify a document for printing is not supported, as changes made in re-rendering may not be accurately reflected.You can use thesx prop'sdisplayPrint field for this purpose instead.SeeMUI System—Display in print for more details.
Using breakpoint helpers
You can use Material UI'sbreakpoint helpers as follows:
import{ useTheme}from'@mui/material/styles';import useMediaQueryfrom'@mui/material/useMediaQuery';functionMyComponent(){const theme=useTheme();const matches=useMediaQuery(theme.breakpoints.up('sm'));return<span>{`theme.breakpoints.up('sm') matches:${matches}`}</span>;}Alternatively, you can use a callback function, accepting the theme as a first argument:
import useMediaQueryfrom'@mui/material/useMediaQuery';functionMyComponent(){const matches=useMediaQuery((theme)=> theme.breakpoints.up('sm'));return<span>{`theme.breakpoints.up('sm') matches:${matches}`}</span>;}⚠️ There isno default theme support, you have to inject it in a parent theme provider.
Using JavaScript syntax
You can usejson2mq to generate media query string from a JavaScript object.
Testing
You need an implementation ofmatchMedia in your test environment.
For instance,jsdom doesn't support it yet. You should polyfill it.Usingcss-mediaquery to emulate it is recommended.
import mediaQueryfrom'css-mediaquery';functioncreateMatchMedia(width){return(query)=>({matches: mediaQuery.match(query,{ width,}),addEventListener:()=>{},removeEventListener:()=>{},});}describe('MyTests',()=>{beforeAll(()=>{ window.matchMedia=createMatchMedia(window.innerWidth);});});Client-side only rendering
To perform the server-side hydration, the hook needs to render twice.A first time withdefaultMatches, the value of the server, and a second time with the resolved value.This double pass rendering cycle comes with a drawback: it's slower.You can set thenoSsr option totrue if you use the returned valueonly client-side.
const matches=useMediaQuery('(min-width:600px)',{noSsr:true});or it can turn it on globally with the theme:
const theme=createTheme({components:{MuiUseMediaQuery:{defaultProps:{noSsr:true,},},},});Note thatnoSsr has no effects when using thecreateRoot() API (the client-side only API introduced in React 18).
Server-side rendering
Server-side rendering and client-side media queries are fundamentally at odds.Be aware of the tradeoff. The support can only be partial.
Try relying on client-side CSS media queries first.For instance, you could use:
If none of the above alternatives are an option, you can proceed reading this section of the documentation.
First, you need to guess the characteristics of the client request, from the server.You have the choice between using:
- User agent. Parse the user agent string of the client to extract information. Usingua-parser-js to parse the user agent is recommended.
- Client hints. Read the hints the client is sending to the server. Be aware that this feature isnot supported everywhere.
Finally, you need to provide an implementation ofmatchMedia to theuseMediaQuery with the previously guessed characteristics.Usingcss-mediaquery to emulate matchMedia is recommended.
For instance on the server-side:
import*as ReactDOMServerfrom'react-dom/server';import parserfrom'ua-parser-js';import mediaQueryfrom'css-mediaquery';import{ createTheme, ThemeProvider}from'@mui/material/styles';functionhandleRender(req, res){const deviceType=parser(req.headers['user-agent']).device.type||'desktop';constssrMatchMedia=(query)=>({matches: mediaQuery.match(query,{// The estimated CSS width of the browser.width: deviceType==='mobile'?'0px':'1024px',}),});const theme=createTheme({components:{// Change the default options of useMediaQueryMuiUseMediaQuery:{defaultProps:{ ssrMatchMedia,},},},});const html= ReactDOMServer.renderToString(<ThemeProvidertheme={theme}><App/></ThemeProvider>,);// …}Make sure you provide the same custom match media implementation to the client-side to guarantee a hydration match.
Migrating fromwithWidth()
ThewithWidth() higher-order component injects the screen width of the page.You can reproduce the same behavior with auseWidth hook:
API
useMediaQuery(query, [options]) => matches
Arguments
query(string |func): A string representing the media query to handle or a callback function accepting the theme (in the context) that returns a string.options(object [optional]):
options.defaultMatches(bool [optional]):Aswindow.matchMedia()is unavailable on the server,it returns a default matches during the first mount. The default value isfalse.options.matchMedia(func [optional]): You can provide your own implementation ofmatchMedia. This can be used for handling an iframe content window.options.noSsr(bool [optional]): Defaults tofalse.To perform the server-side hydration, the hook needs to render twice.A first time withdefaultMatches, the value of the server, and a second time with the resolved value.This double pass rendering cycle comes with a drawback: it's slower.You can set this option totrueif you use the returned valueonly client-side.options.ssrMatchMedia(func [optional]): You can provide your own implementation ofmatchMedia, it's used when rendering server-side.
Note: You can change the default options using thedefault props feature of the theme with theMuiUseMediaQuery key.
Returns
matches: Matches istrue if the document currently matches the media query andfalse when it does not.
Examples
import*as Reactfrom'react';import useMediaQueryfrom'@mui/material/useMediaQuery';exportdefaultfunctionSimpleMediaQuery(){const matches=useMediaQuery('(min-width:600px)');return<span>{`(min-width:600px) matches:${matches}`}</span>;}