React: Theming with Material UI

An introduction to customising your React apps with Material UI themes

Material UI is currently the most popular UI framework for React, with the library providing a range of ready-to-use components out of the box. The library consists of components for layout, navigation, input, feedback and more. Material UI is based on Material Design, a design language that Google fore-fronted originally but is now widely adopted throughout the front-end developer community.

Material Design was originally announced in 2014 and built upon the previous card-based design of Google Now. Material Design is a battle tested design language that comes with support for modern front-end development standards such as responsiveness, theming, mobile-first, and built to be very customisable.

Material UI takes what Material Design has evolved into and provides a library of React components that can be used to build React UX from the ground up. To get a feel of what is possible with Material UI, there are a range of premium themes on the Material UI store that cater for common use cases like admin panels and landing page designs.

This piece acts as an introduction to Material UI for those interested in adopting it in their React projects, and will be heavily focused on theming and the customisability of themes within Material UI.

Theming is the main bottleneck when getting started using the library for the newcomer, but there are a few good tools online to kick start your theming configuration that will be covered further down. The Material UI documentation also has a dedicated section on theming that describes the theming process in depth. This piece however acts as a more tailored approach that will talk through the major concepts along with snippets to quickly set up a theming and styling solution.

If you’ve recently come across Material UI and are wondering whether its worth taking the time to familiarise yourself with the library and ultimately adopt it, there may well be major benefits for doing so. If you fall into the following categories, then Material UI will be very attractive for you:

  • If you have developed ad-hoc React apps that are time consuming to maintain, migrating to Material UI will take away a lot of that maintenance for you. If you have complex input types for example, or a verbose range of breakpoints to manage to get your responsive behaviour just right, or your theme is becoming more complex and harder to maintain, Material UI will abstract those problems and make them a lot easier to manage through their polished APIs and range of useful props provided for each component.

Material UI supports a range of CSS solutions including styled components out of the box, making it easy to migrate existing styles to the library. Although this aids in the migration process, it will become apparent that Material UI’s own styling solution built on top of JSS will be more intuitive and capable to use alongside the library.

  • If your project has gone from an individually managed project to a team based project and you are looking for conformity, Material UI will provide strict conventions that your team will likely already be familiar with, decreasing the learning curve and onboarding process of your project.
  • If you need to prototype new app designs and layouts, Material UIs grid system will more than suffice to play with a flexbox centric layout and responsive behaviour.

To get a feel of the component library at this stage, check out the Components documentation that starts with the Box component (that is essentially a wrapped <div>). There are a range of code snippets for each of the components and live previews for the reader to play with.

You can even plug in own theme configuration into the documentation pages to see how it looks on the live docs. Visit this section to play with primary and secondary colour selections for a live preview courtesy of the docs. We’ll dive into theming much more in the next section.

It’s very simple to get started with Material UI, only requiring the core module to be installed into your project. Along with the core module, I also recommend installing Material UI’s icons package, that make it extremely simple to import and embed SVG icons in your components. Install the packages using yarn:

yarn add @material-ui/core @material-ui/icons

From here, importing Material UI components can be done either by destructuring syntax or by using the full import statement. Consider the following for importing the Grid component:

import { Grid } from '@material-ui/core' // okimport Grid from '@material-ui/core/Grid' // also ok

The former approach is more favoured, especially when it comes to importing a whole range of Material UI components for use:

import { Button, Divider Grid, Typography } from '@material-ui/core'

Click the above components to visit their official documentation. Typography is a text wrapper that handles text variants, such as headers, paragraphs and captions.

Icons can also be imported and embedded with ease:

// embedding material ui icons import AccessAlarmIcon from '@material-ui/icons/AccessAlarm'const AlarmComponent = () => 
<AccessAlarmIcon />

Material UI host a comprehensive range of icons that come in a range of styles, ranging from outlined, filled, rounded, sharp and two-tone.

Although Material UI does offer an Icon component that is designed to handle SVGs and a range of external libraries including Font Awesome, for added ease of development material-ui/icons should be the developer’s first port of call. Check out the Icons Demo to get a feel of how the Icon component supports external libraries.

With this setup in mind, you can now import Material UI components and embed them within your app — but it is most likely that you do not want your React app to feel just like another Google website! This is where theming becomes important. Material UI is fully customisable — you can even disable the trademark ripple effect upon clicking buttons with a disableRipple prop.

The next section dives into the theming solution Material UI offers and walk through the setup process.

Along with the main Material UI components, the documentation also offers a Component API section that references every component used in the Material UI library, along with all props and supported values, starting with the Accordion component. It’s encouraged that the reader familiarise themselves with this section to fully grasp a component’s capabilities, as well as uncover other utility components that may be useful for particular use cases during development.

Theming in Material UI

Ok, let’s now get theming set up in your app in quick-time. Just like a custom React theming solution I have discussed in previous pieces, Material UI relies on React Context to provide a theming object throughout your component hierarchy. This context is provided by the ThemeProvider component of Material UI’s styles:

import { ThemeProvider } from '@material-ui/core/styles'export const App = () => 
<ThemeProvider theme={theme}>
...
</ThemeProvider>

A ThemeProvider naturally can wrap your entire component tree, but the component can also be nested within other ThemeProvider components, overwriting the theme for that subset of components.

A ThemeProvider allows a custom theme object to be provided via the theme prop. If a theme is not provided then ThemeProvider will fall back to the default theme. But what exactly does the default theme entail? Well, the entirety of the default theme is documented here, that outlines the structure of the default theme and everything it is made up of.

Here is a screenshot of the top-level properties of the default theme object:

The top-level properties of the default Material UI theme

With ThemeProvider, this object becomes accessible throughout your React components, and as we will see further down, within styles also. Let’s run through some of the more important and ambiguous values of this default theme:

  • The breakpoints property stores keys and values of various widths that define how responsive your app will be, and are similar to Bootstrap’s Layout breakpoints that exist for the same purpose. By default the xs, sm, md, lg and xl keys are provided along with their corresponding minimum widths in the values property. These values can be accessed throughout your components and styles to conform to the same rules when creating responsive behaviour.
  • mixins provide additional rules for classes that you can inject directly into your styles. The toolbar mixin injects a minimum height to a style for the purpose of headers and toolbars (as the name suggests). The gutters() mixin provides spacing properties.
  • palette is where all your colours are defined. The main colours are the primary and secondary colours, that the majority of Material UI components rely on. Beyond these keys, a subset of light, dark and main colours exist.

You may have noticed properties such as contrastThreshold and tonalOffset within palette. These properties are used to automatically generate a theme based on the main colours provided. For example, given a main primary and secondary colour, the light and dark properties will then be automatically generated along with text colours, that will conform to these thresholds.

Initially you will want to focus on the primary and secondary colour palette. There are a range of tools online to generate a theme object based on these colours. We’ll visit how to automatically generate a theme in the next section.

  • shadows provides an array of shadow configurations of varying intensity.
  • typography configures your fonts, weights, sizes, line heights and letter spacings for all text-based DOM elements, including headers, paragraphs and buttons. The Typography component in-particularly will rely heavily on these values.
  • spacing comes in handy when providing margin and padding values when using Material UIs provided styling solution. spacing is actually a function, denoted by the f e() labelling in the default theme notation. We’ll cover defining styling further down, but this is how you would access spacing in a Material UI styles object:
// padding a `container` style by 2 spacing unitsimport { 
createStyles,
makeStyles,
Theme
} from '@material-ui/core/styles'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
container: {
padding: theme.spacing(2)
},
});

The 3 imports will become very familiar as you build styles for your components.

  • shape.borderRadius is a key value to make your components look less Google-like. Making your shapes more round or more rectangular adds a distinctive feel to your design language, making this value worth playing with.

We’ve now talked about quite a few of the default theme objects, and have built up some key intuition about the range of values that Material UI components rely on. In the next section some key tools will be covered to aid in creating your own theme.

Let’s address some common questions that arise from this stage before moving on:

  • Are we limited to the default theme structure? Absolutely not. More properties of your choosing can be added to the default theme object. although the standard Material UI components will not recognise any ad-hoc theming you may introduce, ThemeProvider will still serve these configurations that you could then leverage in your own components.
  • Do we need to re-define the entire theme when creating an app-specific theme? No, Material UI fills all the missing default values of a given theme object. If you only defined a subset of palette — perhaps only the primary and secondary colours — the rest of the default theme palette will be added (along with the rest of the missing values). This is done by the createMuiTheme we’ll use in the next section.

Creating your Own Theme

Generating your own theme object can be fun, and there are a range of tools available online to aid in the process. This section will document those tools and how to effectively generate primary and secondary colours for your theme. That theme can then be exported as a JSON object and copied into your ThemeProvider’s theme prop.

The official MUI Color Tool is a good starting point for choosing your primary and secondary theme colours. The tool can be found here and documentation here. The Color Tool is easy and intuitive to use, and displays your colours applied to a range of interface elements and text styling.

The following screenshot is of a theme I configured for an administration panel project, with a focus on pastel like colours:

A theme applied to colour tool

Themes can also be shared via the top right link icon. Check out my theme here.

The light and dark variants of this theme were automatically generated based on the two main colours. Because of the ambiguity of these auto-generated colours, the GUI presents every text-to-background-colour pair and flags which are incompatible via the “Not Legible” label.

I have found that these warnings can be over strict — take the primary green colour with the white text variation above. Ultimately you’re the artist of your app and you should make the final call to which colours are suitable for your needs.

Another useful tool is the IYS Theme Editor tool — an open source tool external from the official Material UI maintained resources. This tool is extremely useful; it displays more Material UI components with your theme applied to them, and allows more granular control of the colours used in the left sidebar.

Here is the same theme from above applied to the tool:

The same theme applied to the Theme Editor tool

Notice the button in the bottom right corner — click this button to export a JSON object of your theme, formatted in the same way the default Material UI theme was from earlier.

From here it is straight forward to apply the generated theme. Create a theme.js file to host the theme, and use Material UI’s createMuiTheme utility function to fill in the gaps:

// theme.jsimport { createMuiTheme } from '@material-ui/core/styles'export const theme = createMuiTheme({
"palette": {
"common": {
"black": "#000",
"white": "#fff"
},
"background": {
"paper": "#fff",
"default": "#fafafa"
},
"primary": {
"light": "rgba(89, 210, 188, 1)",
"main": "rgba(20, 160, 140, 1)",
"dark": "rgba(0, 113, 95, 1)",
"contrastText": "#fff"
},
...
}
});
export default theme;

Read more on defining your own themes at the official docs here, or see a more complex example on GitHub here.

With this object now in place, you are free to apply it to ThemeProvider, in your top level <App /> component for instance:

import { ThemeProvider } from '@material-ui/core/styles'
import { theme } from './theme'
const AppWrapped: React.FC = (props: any) => (
<ThemeProvider theme={theme}>
...
</ThemeProvider>
)
export default App;

With your theme now applied to your entire component tree, any Material UI component you plug into your app will now adhere to that customisation.

The last section of this piece will briefly introduce the styling solution of Material UI and how to access your theme properties directly within those styles.

Styling in Material UI

Material UI’s styling solution is available from the @material-ui/styles package. Although Material UI is interoperable with other styling solutions, their solution does appear to be the preferred one with over 1 million weekly downloads at the time of writing.

Three imports will become very familiar to you as you define styles for your components:

import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'

The third import, Theme, is for TypeScript based projects and is not needed if you are opting to use JavaScript instead.

As per the documentation, createStyles does not really do anything, simply acting as an identity function to pass your styles into. makeStyles on the other hand links a stylesheet to a function component utilising the hooks pattern in React.

These three imports can be put together to define a stylesheet that can then be used in a component, like so:

// defining and using a Material UI stylesheetconst useStyles = makeStyles((theme: Theme) =>
createStyles({
container: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
paddingBottom: theme.spacing(1.5)
}
}),
);
export const MyComponent = (props: any) => {

const classes = useStyles();
return (
<Container className={classes.container}>
...
</Container>
);
}

Notice that the entire theme (provided by ThemeProvider) is accessible via the theme argument of makeStyle’s function (we’re defining the stylesheet within an anonymous function). Various theme properties can then be included directly into your classes. theme.spacing has been used in the above example to provide padding to the element.

As another example, the palette object can be accessed to assign a primary or secondary color to a class:

const useStyles = makeStyles((theme: Theme) =>
createStyles({
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
flex: '1 0 auto',
margin: theme.spacing(1),
},
}),
);

The light or dark variant will be determined based on your current assigned theme.

In Summary

This piece has introduced Material UI in the context of theming your React app, with the aim of being able to style the library of Material UI components to your own requirements.

We’ve covered the default theme object and the range of properties that can be configured, as well as the means of providing a theme to your React component hierarchy via the ThemeProvider context. We then looked into how to effectively create your own palette of theme colours using online tools provided by the open source community. Also briefly visited was Material UIs styling solution and how to access your theme config from directly within a stylesheet.

More advanced Material UI articles will be embedded here as they are published!

Programmer and Author. Director @ JKRBInvestments.com. Creator of LearnChineseGrammar.com for iOS.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store