A Better Way to Style Material-UI?

siriwatknp
Bits and Pieces
Published in
6 min readApr 8, 2019

--

This story is about another way of styling Material-UI components instead of using “withStyles”. Now, You all might ask…

Why don’t you use “withStyles” ???

I must say I used it a lot (before), but there are 3 problems that kept annoying me and eventually I decided to change. Let’s see if you are facing the same problems as I was.

Before the explanation, I will use this component as a real-world example for this story.

I want to create a reusable card like the one on the right. On the left is the demo component from Material-UI official site. Here are the problems I face when I tried to override it.

First, too much processes

import { withStyles } from '@material-ui/core/styles';const styles = {
card: {
...
},
media: {
...
},
...otherAttribute
};
<Card className={classes.card}>
...
</Card>
export default withStyles(styles)(NewCard)

This is what it looked like when I used “withStyles” approach. I had to write a lot of codes in order to override it. Image below show the override processes I had to do using “withStyles” approach.

At that moment, my gut asked “why I have to go through a lot of processes just to override a few styles, it should be easier”

This kept annoying me every time I created new components. However, I still followed the official demos.

Second, hard to reuse

There are many situations that you need to copy existing component and create a new. What I found was copying a component that implemented “withStyles” approach to create a new one is sometime painful. In my experience, there was a time that I had to reuse my colleague’s component to create a new one. After I copied and tried to change, I was confused by the structure and naming of the styles object. Finally, I rewrote the new one without using the existing one. (This also related to developer’s skill, not only the approach)

I thought there must be a place where we can define the styles without injecting prop to components.

Third, It is not clean at all

A lot of components contain logic. I think we shouldn’t write styles in it because it will confuse everyone else. I don’t mean small component for demo purpose, I mean the real world components with functions and features. It will be complex, I am sure about that. So, we should try to separate styles from the component as much as possible.

I will show you the “withStyles” approach to turn the left into the right. Take a look at this sandbox.

https://codesandbox.io/s/50l225l964

As you can see, the component rely on the classes prop from “withStyles”. If this component contains logic, it might be hard to move it around since it is attached to the prop.

My Solution?

Here is what I tried to solve.

  • Separate styling from the component by styling in one place.
  • Make the components clean as much as possible (contain only the logic).
  • They have to be easy to move around without a lot of works, however still present the same styles.

To make it happened, I decided to move all of the styling to the app’s theme. In real world, every project must have a well-defined theme to reflect the brand, purpose and feeling. Fortunately, Material-UI provides a way to do it.

https://codesandbox.io/s/m51z3y8plx

Result is the same, but now my component is cleaner because it does not rely on classes prop anymore!. Moreover, it won’t affect any other components because I use the className="MuiEngagementCard--01" as a reference to this component only, next I define each children className to override it from parent.

MuiCard: {
'& .MuiEngagementCard--01': {
transition: '0.3s',
maxWidth: 300,
margin: 'auto',
'&:hover': {
boxShadow: '0 16px 70px -12.125px rgba(0,0,0,0.3)',
},
'& .MuiCardMedia-root': {
paddingTop: '56.25%' // 16:9
}
},
...
}

However, it is still annoying that I have to define className to each children even the children already have className on it. Additionally, our component’s jsx is still dirty because of excess className.

The jsx code using the theme approach (still contain a lot of classNames)

Material-UI has well-defined component structure. However, we can’t do anything about it, in term of referencing by className, because it is hashed behind the scene.

Card DOM that already contain CSS-in-JS className

Material-UI team did a great job on this by enabling us to remove all the hashed from component’s className. Here is the result after I follow their document about globalCSS .

https://codesandbox.io/s/yv7kv238yz

Hooray, this is better. This approach solve a lot of pains that I faced and I love it. At this point you might ask, what about the typography’s className, it is not clean. I totally agree with that, but it is not the same problem here. The typography should be able to define the styles itself because it will definitely be used across the app. If you don’t get it, look at this.

<Card>
...
<CardContent>
<Typography fontWeight={900} fontSize={24}>...</Typography>
<Typography color='grey[500]' lineHeight={1.8} fontSize={12}>...</Typography>
</CardContent>
<Card>

No className on the typography, just properties that define them. I will write more about it in the next story. It is called “System Styling”.

Finally, this is the result that I use this approach to do the styling. It is available in the Material-UI official site. Check it out.

*Notes: every approach has its own pros and cons, the way to maximize the efficiency is to start using, learn and then adapt to match what you seek.

Good News, I have created a free collection of Material-UI components that you can just COPY & PASTE to your project, it is called “MUI Treasury”. Have fun treasuring! https://mui-treasury.com

Mui Treasury Card Page

Thank you for reading. I hope you enjoy.

--

--

Passionate in Design, Theming, React & Firebase. Focus on component reusability.