Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change button props based on breakpoints in Material UI

I'm trying to change the variant and/or size on a material-ui button for different screen sizes. For example, use no variant or size="small" below the "sm" breakpoint and variant="outlined" and/or size="large" above "sm".

Normally, I'd make use withStyles and create a style with theme.breakpoints to affect changes by applying the style to the element using className, however, variant and size are props.

After reading the api, scouring the web, and fiddling extensively, I can't seem to figure out any straight-forward way to change the props based on viewport width.

I've thought about creating a "width-detector" and then using some JS logic to change the button element's props accordingly, but that seems just a bit far out as a solution.

So I'm asking here to see if there is an easier solution out there. Thanks.

like image 274
Kim Avatar asked Sep 24 '18 03:09

Kim


4 Answers

The withWidth HOC is deprecated per Material UI Docs.

This is the approach that works now, with a combination of useTheme and useMediaQuery.

Edit: useTheme is not really required here, since useMediaQuery automatically provides that as an argument.

// import { useTheme } from "@material-ui/core/styles";
import { useMediaQuery } from "@material-ui/core";
...

function ResponsiveButton() {
  // const theme = useTheme();
  // const isSmallScreen = useMediaQuery(theme.breakpoints.down("xs"));
  const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down("xs"));

  const buttonProps = {
    variant: isSmallScreen ? "text" : "outlined",
    size: isSmallScreen ? "small" : "large"
  };
  return (
    <Button {...buttonProps} color="primary">
      Responsive Button
    </Button>
  );
}

export default ResponsiveButton;
like image 105
tajji Avatar answered Oct 02 '22 20:10

tajji


Material UI Docs:

Sometimes you might want to change the React rendering tree based on the breakpoint value. We provide a withWidth() higher-order component for this use case.

withWidth injects a width property into your component that gives you access to the current breakpoint value. This allows you to render different props or content based on screen size.

function ResponsiveButton({ width }) {
  // This is equivalent to theme.breakpoints.down("sm")
  const isSmallScreen = /xs|sm/.test(width);
  const buttonProps = {
    variant: isSmallScreen ? "text" : "outlined",
    size: isSmallScreen ? "small" : "large"
  };
  return (
    <Button {...buttonProps} color="primary">
      Responsive Button
    </Button>
  );
}

export default withWidth()(ResponsiveButton);

Edit Material-UI Render content based on breakpoint

like image 34
Luke Peavey Avatar answered Oct 02 '22 20:10

Luke Peavey


This is my implementation

import Button from "@material-ui/core/Button";
import { useTheme } from "@material-ui/core/styles";
import useMediaQuery from "@material-ui/core/useMediaQuery";
import { useEffect } from "react";

const ResponsiveButton = (props) => {
  const theme = useTheme();
  const desktop = useMediaQuery(theme.breakpoints.up("lg"));
  const tablet = useMediaQuery(theme.breakpoints.up("sm"));
  const mobile = useMediaQuery(theme.breakpoints.up("xs"));

  const sizes = () => {
    if (desktop) return "large";
    if (tablet) return "medium";
    if (mobile) return "small";
  };

  return <Button {...props} size={sizes()}/>;
};
export default ResponsiveButton;
like image 32
Mojtaba Beheshti Avatar answered Oct 02 '22 20:10

Mojtaba Beheshti


To all those who are from 'mui' and not 'material-ui' age, you can create two different components and pass display as an in-line style. Only one will be rendered for a specific screen size.

Refer the example below.

<Typography
  sx={{ display:{sx:'none', sm:'none', md:'block', lg:'block', xl:'block'} }}
>
  This will be rendered only on screens which are medium-sized and above
</Typography>
<Typography
  sx={{ display:{sx:'block', sm:'block', md:'none', lg:'none', xl:'none'} }}
>
  This will be rendered only on screens which are below medium-sized
</Typography>

Not sure if you must mention all the sizes inside display. Maybe you can skip lg and xl once you set md to 'block'.

like image 28
6005 Balaji Avatar answered Oct 01 '22 20:10

6005 Balaji