Adding a MUI5 theme into an application that has a custom emotion theme

Viewed 88

I'm working with an application that has multiple themes. It has a Material UI theme that MUI v4 uses. It also has an emotion theme that non MUI components use.

I was attempting to migrate to MUI v5. MUI v5 uses emotion under the hood.

I declare the MUI theme higher in the hierarchy than I do emotion.

As far as I can tell, this results in some MUI components receiving the emotion theme (meant for non MUI components) from the context, which breaks their styling (theme.spacing missing for instance).

muiTheme = 1
emotionTheme = 2

<MUIThemeProvider theme={muiTheme}> (from @mui/material/styles)
  <EmotionThemeProvider theme={emotionTheme}> (from @emotion/react)
    <MUIComponent> (from @mui/material)
      // theme => 2
    </MUIComponent>
  </EmotionThemeProvider>
</MUIThemeProvider>

Is there any way to ensure that MUI doesn't consume the wrong theme instance? This would give a lot of flexibility when it comes to migration. In the long term there shouldn't be a need to have two themes, but there are two themes now and doing all the refactoring in one go takes a lot of investment. By looking at MUIs documentation, I understood that this is not a usecase the library supports officially.

Some approaches I've considered that don't seem worthwhile:

  • use styled-components for the styled engine to force themes into different contexts
  • add more theme provider declarations to ensure that the correct theme is in context for a given part of the application
  • naively combine the two themes so that they share the same theme object

Approaches that do seem worth considering, but seem to alter the value proposition by a margin:

  • abandon migration to MUI5
  • merge MUI4 theme with application theme before migrating to MUI5
1 Answers

I ran into a very similar issue where I was adding a new theme & components to an existing MUI setup — and had a clash between MUI's theme and the new styled-system theme I was setting up.

The only way I could solve it (while still using emotion) was to swap out the theme being used by the primitive baseline components, using a custom context provider & hook:

const theme = { ...customStuff };

const MyThemeContext = createContext({
  theme,
});

const MyThemeProvider = (props) => (
  <MyThemeContext.Provider value={{ theme }} {...props} />
);

export const useMyTheme = () => useContext(MyThemeContext);

Then put <MyThemeProvider> in the provider stack alongside MUI's, then in your components you'll need something like:

styled.div(({ theme, ...props }) => ({
  const myTheme = useMyTheme();
  // consume `props` however you want, but ignore
  // the `theme` passed as this will be MUI's
  return {
    color: myTheme.color,
  };
})}

This approach worked well for me because I'm using a custom style generator function (very similar to this sx function) and integrated it into primitive components that all other components use under the hood.

This may not be an easy solution for you but figured it might help you or someone else in future ✌️

Related