material-ui: Add useBreakpointValue hook

  • I have searched the issues of this repository and believe that this is not a duplicate.

Summary 💡

Provide a JavaScript API as a counterpart to the CSS sx breakpoint’s API.

Examples 🌈

function MyApp() {
  const elevation = useBreakpointValue({ xs: 1, md: 2 });
  return <AppBar elevation={elevation} sx={{ mb: { xs: 1, md: 2 } }}>...</AppBar>;
}

Internally, useBreakpointValue would use useMediaQuery or useBreakpoint (probably better).

Motivation 🔦

We have started to surface the need for it in https://github.com/mui-org/material-ui/issues/15561#issuecomment-674454140. The hook can be used anytime a value needs to be determined based on the breakpoints. This comment https://github.com/mui-org/material-ui/issues/17000#issuecomment-740159615 triggered the idea around opening this issue. I think that this hook is best for props that are already dependent on JavaScript logic.

One could argue that many of the CSS properties could be made responsive, e.g. the color prop. However, it might be overkill, we could use this hook as an escape hatch.

We have been discussing the idea to remove the <HiddenCss> helper as the Box covers most of the usage thanks to the sx’s display feature. I think that this JS counterpart can solve the <HiddenJs> part of the migration issue: https://github.com/mui-org/material-ui/issues/19704#issuecomment-612034247.

Benchmarks

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 24
  • Comments: 15 (6 by maintainers)

Most upvoted comments

To get the largest matching breakpoint, I use the following:

import { useMediaQuery } from "@mui/material";

export const useBreakpoint = () => {
  const xs = useMediaQuery(theme => theme.breakpoints.up('xs'));
  const sm = useMediaQuery(theme => theme.breakpoints.up('sm'));
  const md = useMediaQuery(theme => theme.breakpoints.up('md'));
  const lg = useMediaQuery(theme => theme.breakpoints.up('lg'));
  const xl = useMediaQuery(theme => theme.breakpoints.up('xl'));

  switch (true) {
    case xl: return 'xl'
    case lg: return 'lg'
    case md: return 'md'
    case sm: return 'sm'
    case xs: return 'xs'
    default: return 'xxs' 
  }
}

What are the use cases for this hook? I don’t consider the initial primer a valid use case. Why would you want to increase elevation depending on the breakpoint? That’s not what you should use elevation for.

What about changing Tabs , AppBar orientation from horizontal to vertical depending on the breakpoint.

This is something Chakra supports out of the box.

The general point here is that currently in MUI it is difficult to make some values responsive, https://github.com/mui-org/material-ui/issues/6140 was a step in the right direction, but it only applies to the Grid component.

Anyway heres a quick one I created for my own use - has not been tested in production to any great extent.


export const useBreakpointValue = <TValue>(
  values: {
    [key in Breakpoint]?: TValue;
  },
) => {
  const matches = {
    xs: useMediaQuery(theme.breakpoints.up(`xs`)),
    sm: useMediaQuery(theme.breakpoints.up(`sm`)),
    md: useMediaQuery(theme.breakpoints.up(`md`)),
    lg: useMediaQuery(theme.breakpoints.up(`lg`)),
    xl: useMediaQuery(theme.breakpoints.up(`xl`)),
  };

  const validBreakpoints = Object.entries(matches)
    .filter(
      ([breakpoint, isMatch]) =>
        Object.keys(values).includes(breakpoint) && isMatch,
    )
    .map(([key]) => key);

  const largestBreakpoint = validBreakpoints.pop();

  if (!largestBreakpoint) {
    return values[0];
  }

  return values[largestBreakpoint];
};