Moe
Moe

Reputation: 1537

How to properly use Styled() with MUI V5

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!

Upvotes: 8

Views: 11207

Answers (1)

Ryan Cogswell
Ryan Cogswell

Reputation: 81156

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

Upvotes: 9

Related Questions