Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material UI 5 class name styles

I migrated from Mui 4 to 5 and wonder how to use class names. If I want to apply certain styles to just one component there is the SX property. However, I'm struggling with using the same class for multiple components. In v4 my code looked like this:

export const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      padding: theme.spacing(1),
      margin: 'auto',
    },
  })
)

I could import this useStyles hook in any component and use it like this:

const classes = useStyles()
...
<div className={classes.root}>...</div>

This docs say, that I can 'override styles with class names', but they don't tell how to do it: https://mui.com/customization/how-to-customize/#overriding-styles-with-class-names

Do I have to put these styles in an external CSS file?

.Button {
  color: black;
}

I would rather define the styles in my ts file.

I also found this migration guide: https://next.material-ui.com/guides/migration-v4/#migrate-makestyles-to-emotion

I don't like approach one, because using this Root wrapper, it is inconvenient to apply a class conditionally. (Especially for typescript there is some overhead) Approach two comes with an external dependency and some boilerplate code.

Ideally I would use styles like this, perhaps with one rapper function around the styles object:

export const root = {
  padding: theme.spacing(1),
  margin: 'auto',
}

<div className={root}>...</div>

Of course, the last approach doesn't work, because className wants a string as input. Does anybody know an alternative with little boilerplate code?

like image 523
Tom Avatar asked Sep 20 '21 08:09

Tom


People also ask

How do I give a className in MUI?

Setup. To customize all the class names generated by Material UI components, create a separate javascript file to use the ClassNameGenerator API. and then import the file at the root of the index before any @mui/* imports.

Is MUI styles deprecated?

@mui/styles is the legacy styling solution for MUI. It is deprecated in v5. It depends on JSS as a styling solution, which is not used in the @mui/material anymore. NOTE: @mui/styles is not compatible with React.


2 Answers

I suggest you take a look at emotion's documentations for details. The sx prop is actually passed to emotion.

You can do something like this:

const sx = {
  "& .MuiDrawer-paper": {
    width: drawerWidth
  }
};
<Drawer sx={sx}/>

Equivalent to MUI v4

const useStyles = makeStyles({
  drawerPaper: {
    width: drawerWidth,
  }
});

const classes = useStyles();

<Drawer
  classes={{
    paper: classes.drawerPaper,
  }}
/>
like image 93
Matthew Kwong Avatar answered Oct 19 '22 17:10

Matthew Kwong


Answering your exact question, there are use cases (I think yours is not one of them and you should use styled components) however for those like me who stumble upon it and want a "exact answer to this question" and not a "do this instead", this is how you achieve to retrieve the class names.

This is so far undocumented.

For functional components, using emotion, here an use case where you have a 3rd party component that expects, not one, but many class names, or where the className property is not where you are meant to pass the property.

import { css, Theme, useTheme } from "@mui/material/styles";
import { css as emotionCss } from "@emotion/css";

const myStyles = {
  basicClass: {
    marginLeft: "1rem",
    marginRight: "1rem",
    paddingLeft: "1rem",
    paddingRight: "1rem",
  },
  optionClass: (theme: Theme) => ({
    [theme.breakpoints.down(theme.breakpoints.values.md)]: {
      display: "none",
    }
  })
}

function MyComponent() {
  cons theme = useTheme();

  // first we need to convert to something emotion can understand
  const basicClass = css(myStyles.basicClass);
  const optionClass = css(myStyles.optionClass(theme));

  // now we can pass to emotion
  const basicClassName = emotionCss(basicClass.styles);
  const optionClassName = emotionCss(optionClass.styles);

  return (
    <ThirdPartyComponent basicClassName={basicClassName} optionClassName={optionClassName} />
  )
}

When you have a Class Component, you need to use the also undocumented withTheme from @mui/material/styles and wrap your class, if you use the theme.

WHEN IT IS NOT AN USE CASE

  1. When your component uses a single className property just use styled components.
import { styled } from "@mui/material/styles";

const ThrirdPartyStyled = styled(ThirdPartyComponent)(({theme}) => ({
  color: theme.palette.success.contrastText
}))
  1. Even if you have dynamic styles
import { styled } from "@mui/material/styles";

interface IThrirdPartyStyledExtraProps {
  fullWidth?: boolean;
}

const ThrirdPartyStyled = styled(ThirdPartyComponent, {
  shouldForwardProp: (prop) => prop !== "fullWidth"
})<IThrirdPartyStyledExtraProps>(({theme, fullWidth}) => ({
  color: theme.palette.success.contrastText,
  width: fullWidth ? "100%" : "auto",
}))

Even if each one has some form of custom color, you just would use "sx" on your new ThrirdPartyStyled.

  1. When you are just trying to reuse a style around (your use case)
const myReusableStyle = {
  color: "red",
}

// better
const MyStyledDiv = styled("div")(myReusableStyle);
// questionable
const MySpanWithoutStyles = styled("span")();

// better
const MyDrawerStyled = styled(Drawer)(myReusableStyle);

function MyComponent() {
  return (
    <MyStyledDiv>
      questionable usage because it is less clean:
      <MySpanWithoutStyles sx={myReusableStyle}>hello</MySpanWithoutStyles>
      <MySpanWithoutStyles sx={myReusableStyle}>world</MySpanWithoutStyles>

      these two are equivalent:
      <MyDrawerStyled />
      <Drawer sx={myReusableStyle} />
    </MyStyledDiv>
  )
}

Now what is "presumably" cool about this is that your style, is just an object now, and you can just import it and use it everywhere without makeStyles or withStyles, supposedly an advantage, even when to be honest, I have never used that of exporting/importing around; the code seems a bit cleaner nevertheless.

You seem to want to use it so all you do is.

export const myStyles {
  // your styles here
}

because this object is equivalent in memory, and it is always the same object, something that is easier to mess up with styles, it should be as effective or even more than your hook, theoretically (if it re-renders often even when setup may be longer), which stores the same function in memory but returns a new object every time.

Now you can use those myStyles everywhere you deem reasonable, either with styled components or by assigning to sx.

You can further optimize, say if it's always a div that you use that is styled the same way, then the styled component MyStyledDiv should be faster, because it is the same and done each time. How much faster is this? According to some sources 55% faster, to me, it is taking 4 weeks of refactor and the JSS compatibility with emotion is bad, all mixed with SSR is making everything unusable and slow and broken, so let's see until then when the whole is refactored.

like image 2
Onza Avatar answered Oct 19 '22 19:10

Onza