Material UI nested theme providers breaks withStyles HOC

All we need is an easy explanation of the problem, so here it is.

I have a React application created with Create React App and I use the @material-ui/core npm package for theming.

To customize components I use the withStyles higher-order component provided by MaterialUI.

According to documentation it supports nested ThemeProviders https://material-ui.com/customization/theming/#nesting-the-theme.

But inside the child ThemeProvider withStyles won’t apply classes.

Here is a basic application demonstrating the issue -> https://codesandbox.io/s/vibrant-tree-eh83d

ExampleComponent.tsx

import React, { FunctionComponent } from "react";
import {
  WithStyles,
  withStyles,
  createStyles,
  StepButton,
  Step,
  Stepper,
  Box
} from "@material-ui/core";

const styles = createStyles({
  button: {
    "& .MuiStepIcon-root.MuiStepIcon-active": {
      fill: "red"
    }
  }
});

interface Props extends WithStyles<typeof styles> {
  title: string;
}

const ExampleComponent: FunctionComponent<Props> = ({ title, classes }) => {
  console.log(title, classes);
  return (
    <Box display="flex" alignItems="center">
      <span>{title}</span>
      <Stepper activeStep={0}>
        <Step>
          <StepButton className={classes.button}>Test</StepButton>;
        </Step>
      </Stepper>
    </Box>
  );
};

export default withStyles(styles)(ExampleComponent);

App.tsx

import * as React from "react";
import { ThemeProvider, createMuiTheme } from "@material-ui/core";
import ExampleComponent from "./ExampleComponent";

const theme = createMuiTheme();

function App() {
  return (
    <ThemeProvider theme={theme}>
      <ExampleComponent title="Root" />
      <ThemeProvider theme={theme}>
        <ExampleComponent title="Nested" />
      </ThemeProvider>
    </ThemeProvider>
  );
}

export default App;

Inside the ExampleComponent I console.log the generated classes object.

I want to use nested ThemeProviders and override classes inside components regardless of the ThemeProvider.
Am I missing something or is this not possible?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

When you are using nested themes, you cannot reliably use Material-UI’s global class names (e.g. .MuiStepIcon-root.MuiStepIcon-active). Within a nested theme, the “Mui…” class names have to be different to avoid conflicting with the CSS classes for the top-level theme since the nested theme will cause some of the CSS for the “Mui…” classes to be different.

You can use the following syntax in order to successfully match the suffixed versions of the Mui class names that occur within nested themes:

const styles = createStyles({
  button: {
    '& [class*="MuiStepIcon-root"][class*="MuiStepIcon-active"]': {
      fill: "red"
    }
  }
});

Edit withStyles with nested themes

Related answer:

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply