DEV Community

Jimmy (Yanxiang) Lan
Jimmy (Yanxiang) Lan

Posted on

Start Customizing Material UI Like a Pro: Beginner's Guide

Nowadays, Material UI is one of the most popular UI libraries for React. By installing the library, developers can introduce Material Design System onto their web pages without some tedious effort.

For example, adding a text field and a button is as easy as:

import { Box, Button, TextField } from "@mui/material";
Enter fullscreen mode Exit fullscreen mode

Then using it as:

<Box>
    <TextField label="name" variant="standard" />
    <Button variant="contained">Submit</Button>
</Box>
Enter fullscreen mode Exit fullscreen mode

Output:

material-design text field and button

However, despite having excellent documentation, further customizing Material UI components to fit a company's branding is not trivial. Due to this, web developers new to Material UI may find the library overwhelming. This blog post will briefly address some difficulties a developer may face when getting started with the library. While doing this, we will also go over a quick example to customize a Material UI TextField component.

Note: This post is a quick start guide for developers new to Material UI. It is NOT documentation for Material UI. For the official documentation, please refer to Material UI's official website. In addition, the post is written when Material UI v5 is the latest stable version. Please consider the timeliness of information when reading this blog post.

How to Style?

There are so many ways to style with Material UI. The library gives enough freedom to developers, so you can choose how to style your web page based on your circumstances.

Stying with Styling Libraries

If you are familiar with other styling libraries, such as Styled Components, Tailwind CSS, or Emotion, Material UI has good support for these styling engines. You can start using these libraries by installing them normally.

After installing your preferred styling library, please keep an eye on the <style> tags under the <head> of your page. We can utilize the "inspect elements" feature on the browser and take a look:

html style tags from

The style tags in the image above are injected by Material UI. Depending on your styling library, if Material UI injects styles after your chosen styling library, some of your custom styles might be overridden unexpectedly.

This is one of the most common problems that developers may encounter with Material UI. To make it even worse, there is no checker in place to log error messages or warnings. Therefore, it is important to realize the CSS injection order and make corrections before we continue.

Assuming in our index.js file, we have:

import { App } from "./App";

const root = ReactDOM.createRoot(
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

To correct the injection order, we can utilize the StyledEngineProvider component exported by Material UI, as follows:

import { StyledEngineProvider } from "@mui/material";

// ...

root.render(
  <StyledEngineProvider injectFirst>
    <App />
  </StyledEngineProvider>
)
Enter fullscreen mode Exit fullscreen mode

This will fix the injection order, allowing the Material UI styles to appear first in the <head> section, followed by your custom styles.

Styling with Plain CSS

If you are looking for the traditional approach, you may also style with plain CSS. Just add a class name to your component and style it with a CSS file.

Example.style.css

.button {
  background-color: #000;
}
Enter fullscreen mode Exit fullscreen mode

Example.jsx

import "./Example.style.css";

export const Example = () => {
  return (
    <Box>
      <TextField label="name" variant="standard" />
      <Button variant="contained" className="button">
        Submit
      </Button>
    </Box>
  )
}
Enter fullscreen mode Exit fullscreen mode

Output:

example web page with a black background button

Styling with Material UI Systems

In addition to the two methods above, you can add some quick styles with helpers exported by Material UI. Some people prefer this method more than the previous ones, as it may be the most intuitive for one to work with all Material UI features.

Each component exported by Material UI accepts an sx prop. You can add custom CSS here, as follows:

<Box>
  <TextField label="name" variant="standard" />
  <Button variant="contained" sx={{ backgroundColor: "#000" }}>
    Submit
  </Button>
</Box>
Enter fullscreen mode Exit fullscreen mode

This will produce the same effect as in the plain CSS section.

The sx prop allows more features, such as accessing the global theme element that you may configure with ThemeProvider exported by Material UI. Read more about themes in the Material UI official documentation.

<Box sx={{ margin: 16, padding: 8 }}>
  <TextField label="name" variant="standard" />
  <Button
    variant="contained"
    sx={{
      backgroundColor: "#000",
      marginLeft: (theme) => theme.spacing(2),
    }}
  >
    Submit
  </Button>
</Box>
Enter fullscreen mode Exit fullscreen mode

Output:

text field and button with left margin

Note the left margin of the button.

In fact, even the Box element that we are using up to this point is part of the "Material UI System" components. "Box" is a wrapper component for styling purposes, while one can also use other components such as "Container", "Grid", or "Stack" to produce complex layouts. For example,

<Stack spacing={2}>
  <TextField label="name" variant="standard" />
  <Button variant="contained">Submit</Button>
</Stack>
Enter fullscreen mode Exit fullscreen mode

Output:

Stacked text field and button

Learn more about Material UI System here: Official MUI Documentation.

Styling with "makeStyles"

If you are a developer migrating from Material UI v4, you may ask, "how about styling with makeStyles and useStyles?" While these utilities are deprecated in Material UI v5 and won't work unless you install deprecated packages, we can always make these utility functions ourselves.

const useStyles = () => ({
  button: {
    backgroundColor: "#000",
  },
});
Enter fullscreen mode Exit fullscreen mode

and remember the sx prop that we introduced above? We can still do classes = useStyles();, then assign classes.button to the sx prop.

Example: Customizing Text Field

Styling work in the real world often needs to match a branding guideline, so it will be more complicated than just using Material UI out of the box. Suppose that we want to have a text field that looks like the following:

states of a customized text field

Of course, looking at all variants of the Material UI text field, it is quite different:

material ui: variants of text fields

In general, we follow the following steps when customizing a Material UI component:

  1. Determine the scope of the customization;
  2. Determine the component states/classes that will be affected;
  3. Complete the customization

Determine the scope of the customization

First, we need to understand the scope of our customization. Depending on the answer, one might choose to solve the customization using one of the following methods:

i. Styling from the ground up using the base package. Material UI has a base package, which hosts the "unstyled" MUI components. This is beyond the scope of this blog post;
ii. Expanding a Material UI component to access the lower-level components. For example, the Material UI TextField component is an abstraction of FormControl, InputLabel, FilledInput, OutlinedInput, Input, and FormHelperText (Source: Material UI Text Field API Docs).
iii. Styling directly on the component using properties and class names accessible.

In our case, we can style directly on the TextField component.

Determine the component states/classes that will be affected

We will customize the hover state and the focused state of the text field. Note that Material UI generates a list of global CSS class names. For the focused state, we should customize using the .Mui-focused class.

Complete the customization

We will base our customization off a Filled Input.

CustomizedTextField.jsx

We start by adding some styles for the text field.

const getRootStyles = (theme) => ({
  "& .MuiFilledInput-root": {
    border: `1px solid ${theme.palette.grey["A200"]}`,
    overflow: "hidden",
    borderRadius: theme.spacing(0.5),
    transition: theme.transitions.create([
      "border-color",
      "background-color",
      "box-shadow",
    ]),
    "&:hover": {
      backgroundColor: "transparent",
    },
    "&.Mui-focused": {
      backgroundColor: "transparent",
      borderColor: theme.palette.primary.main,
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Note that we are using the theme object. The theme object is managed the the Material UI library, and we can get theme by calling the hook useTheme. useTheme is exported by "@mui/material".

const CustomizedTextField = (props) => {
  const theme = useTheme();
  const root = getRootStyles();
  const { sx, InputProps, ...otherProps } = props;

  return (
    <TextField
      variant="filled"
      InputProps={{ disableUnderline: true, ...InputProps }}
      sx={{ ...root, ...sx }}
      {...otherProps}
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

and finally

export { CustomizedTextField }
Enter fullscreen mode Exit fullscreen mode

If you wonder where we get class names such as .MuiFilledInput-root, we can get them by using the official documentation or the "inspect element" function in your browser.

We can now import CustomizedTextField from CustomizedTextField.jsx and start using it.

The icon at the end of the text field came from the inputAdornment prop. One can specify the input adornment when they use the custom component that we just exported. For example,

import { InputAdornment } from "@mui/material"
import { Badge } from "@mui/icons-material";
import { CustomizedTextField } from "./CustomizedTextField"
Enter fullscreen mode Exit fullscreen mode
<CustomizedTextField
  label="name"
  InputProps={{
    endAdornment: (
      <InputAdornment position="end">
        <Badge style={{ color: "#C3C3C3" }} />
      </InputAdornment>
    ),
  }}
/>
Enter fullscreen mode Exit fullscreen mode

We're all set! We have now completed the custom text field that we wanted at the beginning of this section.

Image description

Final Words

I hope this blog post is helpful for your Material UI journey. Thank you for your time checking out my post!

Top comments (1)

Collapse
 
brense profile image
Rense Bakker

You can also do theme overrides (including default props) at app level, using the components property of the createTheme() function. mui.com/material-ui/customization/...