Using Material UI Themes in React

We create open-source because we love it, and we share our finding so everyone else can benefit as well.

Using Material UI Themes in React

An additional theme being added to your app, not only adds character to your application, but can also increase accessibility to app. While the Material-UI documentation hasn’t always been clear on how to accomplish this, it’s actually an easy system to become accustom to. We’ll quickly go over how to implement themes and how to avoid the problems that can arise when doing so, as well as bonus of applying your global css with Material-UI.

To keep this article short, I’m going to assume anyone reading, is already aware of how to use the Material-UI styling system, and with hooks.

Creating a Material UI Theme

Creating a theme in Material UI is quite simple, as the concept is creating a custom theme object using the creatMuiTheme constructor, and using that as your template for your theme. To create such an object, we can do the following:

import red from '@material-ui/core/colors/red'
import lightBlue from '@material-ui/core/colors/lightBlue'
import grey from '@material-ui/core/colors/grey'
import { createMuiTheme } from '@material-ui/core'

const DarkTheme = createMuiTheme({
  themeName: 'My Dark Theme',
  palette: {
    type: 'dark',
    primary: primary: {
      light: lightBlue[500],
      main: lightBlue[600],
      dark: lightBlue[800],
      contrastText: grey[50]
    },
    secondary: red,
    error: red
  },
  overrides: {
    MuiTooltip: {
      root: {
        color: grey[800]
      }
    }
  },
  spacing: 4
})
How the app sees the
color objects

Each color the MUI components use, can be changed by setting it in the palette object, with the primary, secondary, and error objects being the common hues used. You may notice that the primary and secondary objects are styled differently. When you want to set the colors explicitly, you can use the method used with the primary color, by setting the main and contrastText colors. You can also set the light and dark colors to be more specific in certain cases, but unlike the previous, they are not required. If you only want to set a color for one of these objects, you can use the color object instead, and the main, light, dark, and contrastText values will be set automatically, which you can see in the screenshot.

Setting the colors of the theme is only part of the appeal of using a theme. The other benefits come from the component overrides, which allow you to specifically style each MUI component, as well as the options for the text, and spacing. Starting with the overrides, each one is based on the styles given to each component while used in-app, and the CSS API of these components.

The entire MUI library is styled around these different theme settings, so you can style each MUI objects for your entire app from the theme. For instance, in the example theme, we set the styling for the MUIToolTip-root class, making any ToolTip component in the app take on this styling. The best way to know the component and style naming to use, is to view each component in the React Developer Tools, and check the classes and the styles applied.

The theme’s spacing object is by far the most powerful tool in the theme, even though some may not see it as such. If you are an advocate of practices taught by Adam Wathan, in his book, Refactoring UI, you will automatically know the benefit of this feature, otherwise the overall idea is normalizing your margins and spacings by basing them on multiples of a base size, so that every component ends up with equal spacing.

So with material UI themes, you set your base spacing size, and each component can then be spaced based on that base size. In the example above, we use a base of 4px, so components will be spaced with 4, 8, 12, 16, etc px spacing, and we can do this easily as we style a component with Material UI:

const useStyles = makeStyles(theme => ({
    root: {
      color: theme.palette.primary.main,
      cursor: 'pointer',
      fontSize: 16,
      margin: theme.spacing(0, 1), // 0px, 4px
      padding: theme.spacing(2) // 8px
    }
  }))

Now that we know how to create our theme, let’s look at how to implement it.

Adding your Material Theme

Implementation is very simple concept, using the ThemeProvider component, so we can add our theme using the following:

<ThemeProvider theme={MyDarkTheme}>
  <App />
</ThemeProvider>

With our theme object already created, this is all we need to pass the theme to all of the child MUI objects. The example theme above used a very limited amount of options available for the theme, but now that the ThemeProvider is added, we can see what options are available for use to change. Since each object in our theme is taking the place of the objects in the default theme, we can see what objects haven’t been replaced, and set them in our theme, if we please. We can see the available objects by viewing the ThemeProvider theme props in the React DevTools:

Any of the objects found in the ThemeProvider theme props, can be used in our theme for a better overall customization.

Nested Material-UI Themes

Normally, when you want to create a theme, you want to create at least two themes, a dark and light theme, but what if you have a section of the app that needs its own static theme? It’s at that point you will need a nested theme provider. It’s as simple as you would expect, wrap an additional ThemeProvider object around the child objects that need to take on that theme. An issue comes with this implementation, as conflicts that can happen due to multiple providers colliding.

Breaking Styles with Nested Themes

The most common issue is finding a component that isn’t taking on the theme, or is using the base styles over either theme (translucent background). If you look at the component in the devtools inspector, you’ll find the style for the theme has been applied, but is overridden by the base component style. Another indication, is if you find that the components are no longer updating the styles due to a collision of class names, where prior to MUI 4.x, the fix was wrapping the children with a JssProvider component.

To get around this, you need to specify a class prefix for one of the ThemeProviders, allowing them work independently:

import { createGenerateClassName, StylesProvider } from '@material-ui/core/styles'

const generateClassName = createGenerateClassName({
  productionClassPrefix: 'App'
})

return (
  <StylesProvider generateClassName={generateClassName}>
    <ThemeProvider theme={MyDarkTheme}>
      <App />
    </ThemeProvider>
  </StylesProvider>
)

Doing this will allow you to get around this collision, just keep in mind that you will need to do this for each of your nested ThemeProviders to avoid any conflicts in the future.

BONUS: Global Styling

It's not very obvious how to set global styling with MUI without having to use an external library or the built-in styled-components library. As a bonus, let's go over how to accomplish this, so you can restrict your styling to a single library. First, we will start by creating a Global Styling object that will provide the styling.
import { makeStyles } from '@material-ui/core/styles'

const AppGlobalCss = props => {

  const useStyles = makeStyles({
    '@global': {
      '#app, html': {
        height: '100%'
      },
      'ul::-webkit-scrollbar': { width: '0 !important' },
      'div::-webkit-scrollbar': { width: '0 !important' }
    }
  })

  useStyles()

  return props.children
}

As you can see, we’re using a makeStyles hook to apply the styles to the child component. While we could add this to a component’s style directly, but since we’re applying to the root of the app, we want something that can be used in a class-based root component, so we made a Higher-Order-Component instead.

Now that we have our HOC styling component ready, here is how to apply it to our app:

return (
  <StylesProvider generateClassName={generateClassName}>
    <ThemeProvider theme={MyDarkTheme}>
      <GlobalAppCss>
        <App />
      </GlobalAppCss>
    </ThemeProvider>
  </StylesProvider>
)

It’s as simple as that, and you have styled your entire app using Material-UI, with no need for anything else.

 

No Comments

Add your comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.