8

I am having an issue trying to use MUI Styled () on bigger scale: can someone take a look at the code we used to use in prior versions and let me know how to replicate it in MUI V5.

Old way:

const useStyles = makeStyles((theme) => ({
  root: {
    backgroundColor: "#fdfdff",
  },
  pageHeader: {
    padding: theme.spacing(4),
    display: "flex",
    marginBottom: theme.spacing,
  },
  pageIcon: {
    display: "inline-block",
    padding: theme.spacing(2),
    color: "#3c44b1",
  },
  pageTitle: {
    paddingLeft: theme.spacing(4),
    "& .MuiTypography-subtitle2": {
      opacity: "0.6",
    },
  },
}));
export default function PageHeader(props) {
  const classes = useStyles();
  const { title, subTitle, icon } = props;
  return (
    <Paper elevation={0} square className={classes.root}>
      <div className={classes.pageHeader}>
        <Card className={classes.pageIcon}>{icon}</Card>
        <div className={classes.pageTitle}>
          <Typography variant="h6" component="div">
            {title}
          </Typography>
          <Typography variant="subtitle2" component="div">
            {subTitle}
          </Typography>
        </div>
      </div>
    </Paper>
  );
}

Edit v4 example

My attempt to accomplish the same in MUI V5 is not working properly. it renders but it doesn't look the same and it's all over the place.

const rootStyle = styled("div")({
  backgroundColor: "#fdfdff",
});
const headerStyle = styled("div")(({ theme }) => ({
  padding: theme.spacing(4),
  display: "flex",
  marginBottom: theme.spacing,
}));
const iconStyle = styled("div")(({ theme }) => ({
  display: "inline-block",
  padding: theme.spacing(2),
  color: "#3c44b1",
}));
const titleStyle = styled("div")(({ theme }) => ({
  paddingLeft: theme.spacing(4),
  "& .MuiTypography-subtitle2": {
    opacity: "0.6",
  },
}));

export default function PageHeader(props) {
  const { title, subTitle, icon } = props;
  return (
    <rootStyle>
      <Paper elevation={0} square>
        <headerStyle>
          <iconStyle>
            <Card>{icon}</Card>
          </iconStyle>
          <titleStyle>
            <Typography variant="h6" component="div">
              {title}
            </Typography>
            <Typography variant="subtitle2" component="div">
              {subTitle}
            </Typography>
          </titleStyle>
        </headerStyle>
      </Paper>
    </rootStyle>
  );
}

I am new to MUI and there are not a lot of examples out there that cover this. I truly appreciate your help!

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
Moe
  • 1,427
  • 4
  • 34
  • 54
  • [This](https://stackoverflow.com/a/69723309/9449426) should solve your problem. – NearHuscarl Nov 06 '21 at 02:13
  • Here the docs for the new way of spacing: https://mui.com/system/spacing/ – ale917k Nov 06 '21 at 02:26
  • I think the part in App.js is correct, the question now how can we replace makeStyles with the new way. I am not just after spacing; I am after how to use theme in v5 – Moe Nov 06 '21 at 02:33
  • @Moe you can use `sx` prop/`styled` in v5. There is a bunch of examples on the docs to show you how to use them. – NearHuscarl Nov 06 '21 at 03:55
  • I updated the question; can you provide an example of how to replicate what I have using styled – Moe Nov 06 '21 at 13:55
  • @NearHuscarl can you a look at my updated question and let me know your thoughts? – Moe Nov 07 '21 at 01:07

1 Answers1

8

Below is a v5 version of your code with the same look as the v4 version. I added default values for the props just for demonstration purposes.

You had two main issues:

  1. You added additional div layers for the styling rather than styling the elements that originally received the styles (e.g. Paper, Card).

  2. You assigned the styled divs to variable names that start with a lowercase letter which caused them to be rendered as DOM tags rather than components (so the styling would have been completely ignored).

From https://reactjs.org/docs/components-and-props.html#rendering-a-component:

React treats components starting with lowercase letters as DOM tags.

import Paper from "@mui/material/Paper";
import Card from "@mui/material/Card";
import Typography from "@mui/material/Typography";
import { styled } from "@mui/material/styles";
import PersonIcon from "@mui/icons-material/Person";

const StyledPaper = styled(Paper)({
  backgroundColor: "#fdfdff"
});
const HeaderDiv = styled("div")(({ theme }) => ({
  padding: theme.spacing(4),
  display: "flex",
  marginBottom: theme.spacing
}));
const StyledCard = styled(Card)(({ theme }) => ({
  display: "inline-block",
  padding: theme.spacing(2),
  color: "#3c44b1"
}));
const TitleDiv = styled("div")(({ theme }) => ({
  paddingLeft: theme.spacing(4),
  "& .MuiTypography-subtitle2": {
    opacity: "0.6"
  }
}));

export default function PageHeader(props) {
  const {
    title = "Title",
    subTitle = "sub-title",
    icon = <PersonIcon />
  } = props;
  return (
    <StyledPaper elevation={0} square>
      <HeaderDiv>
        <StyledCard>{icon}</StyledCard>
        <TitleDiv>
          <Typography variant="h6" component="div">
            {title}
          </Typography>
          <Typography variant="subtitle2" component="div">
            {subTitle}
          </Typography>
        </TitleDiv>
      </HeaderDiv>
    </StyledPaper>
  );
}

Edit v5 example

An alternative (and much more concise) way to convert the v4 code to v5 is to use the sx prop:

import Paper from "@mui/material/Paper";
import Card from "@mui/material/Card";
import Typography from "@mui/material/Typography";
import PersonIcon from "@mui/icons-material/Person";
import Box from "@mui/material/Box";

export default function PageHeader(props) {
  const {
    title = "Title",
    subTitle = "sub-title",
    icon = <PersonIcon />
  } = props;
  return (
    <Paper elevation={0} square sx={{ bgcolor: "#fdfdff" }}>
      <Box sx={{ p: 4, display: "flex", mb: 1 }}>
        <Card sx={{ display: "inline-block", p: 2, color: "#3c44b1" }}>
          {icon}
        </Card>
        <Box sx={{ pl: 4, "& .MuiTypography-subtitle2": { opacity: 0.6 } }}>
          <Typography variant="h6" component="div">
            {title}
          </Typography>
          <Typography variant="subtitle2" component="div">
            {subTitle}
          </Typography>
        </Box>
      </Box>
    </Paper>
  );
}

Edit v5 example using sx

Here is one more option using a single styled call, though in my opinion this would be more brittle to maintain than the other options:

import Paper from "@mui/material/Paper";
import Card from "@mui/material/Card";
import Typography from "@mui/material/Typography";
import { styled } from "@mui/material/styles";
import PersonIcon from "@mui/icons-material/Person";

const StyledPaper = styled(Paper)(({ theme }) => ({
  backgroundColor: "#fdfdff",
  "& > div": {
    padding: theme.spacing(4),
    display: "flex",
    marginBottom: theme.spacing(1),
    "& .MuiCard-root": {
      display: "inline-block",
      padding: theme.spacing(2),
      color: "#3c44b1"
    },
    "& > div": {
      paddingLeft: theme.spacing(4),
      "& .MuiTypography-subtitle2": {
        opacity: "0.6"
      }
    }
  }
}));

export default function PageHeader(props) {
  const {
    title = "Title",
    subTitle = "sub-title",
    icon = <PersonIcon />
  } = props;
  return (
    <StyledPaper elevation={0} square>
      <div>
        <Card>{icon}</Card>
        <div>
          <Typography variant="h6" component="div">
            {title}
          </Typography>
          <Typography variant="subtitle2" component="div">
            {subTitle}
          </Typography>
        </div>
      </div>
    </StyledPaper>
  );
}

Edit v5 example with one styled call

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
  • thanks for the detailed explanation: another question if I may, is there a way to consolidate Styled components simliary as we did before with useStyles (old way). passing the theme in each const component is repetitive. is there a way to avoid it? – Moe Nov 07 '21 at 03:48
  • @Moe There isn't a straightforward way to avoid passing the theme for each `styled` call unless you do all the styling via a single `styled` call and then target the descendant elements in the styles, but I think that would result in more complicated styles that are a little more difficult to maintain. One alternative is to use the `sx` prop instead of `styled` -- I've added an example of that. – Ryan Cogswell Nov 07 '21 at 18:27
  • Thank you so much again! I tried the single style and I wasn't able to call the component. so I asked to know the right and best way of doing it. So to summarize, either break them into to small components or use sx to style. – Moe Nov 07 '21 at 19:22
  • @Moe I've added an additional example showing one way of doing it with a single `styled` call. There isn't really one "right and best way" to style things -- there are many options with different trade-offs depending on the specifics of your scenario. – Ryan Cogswell Nov 07 '21 at 19:46
  • Thanks so much, I think the community will appreciate your answers as well. I truly appreciate you taking the time to help us with this. – Moe Nov 07 '21 at 20:59
  • hello @Ryan, can you take a look at this post and let me know your thoughts. https://stackoverflow.com/questions/70140504/how-to-pass-a-custom-style-to-mui-v5-styled-component/70141256?noredirect=1#comment123989959_70141256 – Moe Nov 28 '21 at 15:29