We will see how to style Material UI components with the library styled-components. Among the edge cases we will cover are: overriding Material UI's theme, prioritize the CSS rules of styled components and override classes other than root of Material UI components.
You can play with the code snippets present in this tutorial with the following codesandbox link: https://codesandbox.io/embed/6lmnv4457z
Material UI is a great component library that emulates Google’s Material Design. I choose to use it on each of my React web apps. And I am not alone: with more than 42k stars on github, it is the most popular component library for React.
Styled-components is another great library used to style React components. We do so by defining React “styled” components without CSS classes. Indeed, these styled components take CSS rule blocks as input. With 20k stars on github, it seems to be a no-brainer for many other web app developers too.
Some of the advantages of styled-components:
Material UI was designed to be used from the get-go with its own styling solution, implemented over JSS, as opposed to using styled-components.
Fortunately, Material UI has made it simple to use other styling solutions. Therefore, if you want to leverage the advantages of both Material UI and styled-components, wait no longer and read the rest of this article.
Even though our objective is to style our web app using styled-components, we should not be afraid of using Material UI’s theme feature. The theme specifies the default style rules of the MUI components, such as color, level of shadow, etc…The theme enables us to create a pretty good base for our style. To illustrate the usage of Material UI’s theme, we will give a red background color to all our buttons, with a green background color on hover.
First create a theme.js file in your root folder
import { createMuiTheme } from "@material-ui/core/styles";
export default createMuiTheme({
overrides: {
MuiButton: {
root: {
fontWeight: "bold",
backgroundColor: "red",
margin: "10px",
"&:hover": {
backgroundColor: "green"
}
}
}
}
});
Then, we have to provide the theme defined above to the app, using the MuiThemeProvider. The MuiThemeProvider takes as prop a theme and injects it into the application. We encapsulate our app in theMuiThemeProvide as follow
import React, { Component } from "react";
import ReactDOM from "react-dom";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import Button from "@material-ui/core/Button";
import theme from "./theme";
class App extends Component<> {
render() {
return (
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(, rootElement);
As you have just seen, to customize a specific instance of material UI components within our app, we provided to our theme the name and class of the component. This name and the class can be found in the CSS section of the API page of the Material UI components, for example https://material-ui.com/api/button/#css.
The styled() method from styled-components takes as input a React component and outputs another React component with a different style. It is useful as it enables you to style your component using the exact same syntax as in your typical CSS stylesheet. It works as follow:
import styled from "styled-components";
import Button from "@material-ui/core/Button";
export default styled(Button)`
color: white;
background-color: blue;
height: 80px;
`;
❗️❗️❗️ Warning ❗️❗️❗️ : if you made your app exactly as in the previous steps, you should see that some of the CSS rules defined in the styled component are overridden by the defaults of Material UI, such as color andbackground-color. This is because the CSS injected by Material-UI to style a component has the highest specificity possible as the <link> is injected at the bottom of the <head> in the index.html file, in order to ensure the components always render correctly.
You might, however, want to override these styles if you intend to fully use styled-components. This leads us to the next step.
1st method: increase specificity by repeating the class name
To ensure styled-components styles are applied before JSS styles, you can use use the ampersand (&) character in styled-components, which is equivalent to repeating the class name. The ampersand character is used to refer back to the main component. This solution applied to our example gives the following
import styled from "styled-components";
import Button from "@material-ui/core/Button";
export default styled(Button)`
&& {
color: white;
background-color: blue;
height: 80px;
}
`;
The disadvantage of this approach is that you have to use it for every styled component whose style specificity does not suit your needs. Not very DRY (Don't Repeat Yourself)!
2nd method: modify the CSS injection order
JSS provides a mechanism to handle specificity issues. By adjusting the placement of the insertionPoint within your HTML <head>, you can control the order in which the CSS rules are applied to your components.
The simplest approach is to add an HTML comment that determines where JSS will inject the styles. However, you may have been using create-react-app to kickstart your project. Create React App strips HTML comments when creating the production build. To get around the issue, you can provide a DOM element as the JSS insertion point.
First, place the insertion point as a DOM element at the top of <head>:
<head>
<noscript id="jss-insertion-point"></noscript>
<link href="..." />
</head>
Then, import JssProvider and other utility functions in your root component. You have to wrap your root component as follow:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import JssProvider from "react-jss/lib/JssProvider";
import { create } from "jss";
import { createGenerateClassName, jssPreset } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import theme from "./theme";
import SimpleStyledButton from "./SimpleStyledButton";
const generateClassName = createGenerateClassName();
const jss = create({
...jssPreset(),
// Define a custom insertion for injecting the JSS styles in the DOM
insertionPoint: document.getElementById("jss-insertion-point")
});
class App extends Component<> {
render() {
return (
Simple button styled with styled-components
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(, rootElement);
Your styled components are still components: you can pass them props to style them dynamically! However, to avoid warnings related to props passed directly to the underlying DOM element, you have to decompose what props you actually pass to the overriden Material UI component. An example of such decomposition follows:
import React from "react";
import styled from "styled-components";
import Button from "@material-ui/core/Button";
export default styled(({ color, ...otherProps }) => <Button {...otherProps} />)`
color: ${props => props.color};
`;
There are two things you might do while doing a “complex” styling of your Material UI component:
The most reliable approach is to use the classes property to introduce an override style, and then style it with higher specificity with selectors. Note that the styled-components' CSS preprocessor is stylis, which supports css-like syntax for automatically nesting styles.
The following example overrides the label and disabled styles of Button in addition to the custom styles on the button itself:
import React from "react";
import styled from "styled-components";
import Button from "@material-ui/core/Button";
export default styled(({ color, ...otherProps }) => (
<Button {...otherProps} classes={{ disabled: "disabled", label: "label" }} />
))`
color: ${props => props.color};
& .label {
background-color: purple;
}
&.disabled {
color: black;
background-color: orange;
.label {
background-color: green;
}
}
`;
I believe I have shown you all the possible edge-cases that can happen when using the Material UI with styled components. While I chose Material UI as my component collection, you should be able to use the same styling techniques for any other component library.
Therefore, you should not have any problem styling your components in a React app from now on!