Search code examples
reactjstypescriptmaterial-uiemotion

Cannot use mui theme with emotion css prop


I am trying to implement a dark theme in the web app and moved my styling from just styles.css with overwriting using !important to emotion css prop.

Here is the code from App.tsx, where I create my theme and use ThemeProvider

const theme = createTheme({
  typography: {
    allVariants: {
      fontFamily: 'SFCompactDisplay',
    },
  },
  palette: {
    primary: {
      main: '#0052cc',
    },
  },
});

console.log(theme);

const App: React.FC = () => (
  <ThemeProvider theme={theme}>
    <Switch>
      <Route path="/login" component={Login} exact />
      <ProtectedRoute path="/" component={Dashboard} exact />
      {/* <Route path="/register" component={Register} /> */}
      <Redirect to="/" />
    </Switch>
    <Toaster
      position="top-right"
      toastOptions={{
        style: {
          fontWeight: 400,
        },
      }}
    />
  </ThemeProvider>
);

Also, here is the code from css.ts, which I then use in one of the components

export const splitContainer = (theme) => {
  console.log(theme);
};

export const content: CSSWithTheme = (theme) => ({
  maxWidth: '800px',
  width: '100%',
  [theme.breakpoints.down('md')]: {
    padding: '0 24px',
    maxWidth: '100%',
  },
});

I get an error stating that any property of theme is undefined. I consoled logged both themes, the first one, in App looks as normal MUI theme with breakpoints, pallete properties, but the one in css.ts looks like this:

enter image description here

I use the splitContainer in Dashboard component

  return (
    <Box css={css.splitContainer}>
      <SideBar tab={tab} setTab={setTab} />
      <Box className="container-main-overview">
        {tab === 1 && <MachineList />}
        {tab === 4 && <AddInstance />}
        {tab === 5 && <Support />}
      </Box>
    </Box>
  );

The styles work, however when I try to use the theme, I get undefined error


Solution

  • As per @Ryan Cogswell mentioned, you need to declare the Emotion theme provider as well. Since Emotion's theme is an empty object, you can pass the MUI theme as is. If you use typescript, you will need to extend emotion's theme interface. If you need to handle dark mode, you can have your own theme provider (based on React's context API) to switch the light and dark themes and to combine the above mentioned two, MUI and emotion providers.

    Here is a small example for the context provider (without the context definition)

    const ThemeProvider: FC<ThemeProviderProps> = ({ children }) => {
      const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
      const defaultTheme = useMemo<ThemeContextProps['themeMode']>(
        () => (prefersDarkMode ? 'dark' : 'light'),
        [prefersDarkMode],
      );
    
      const [themeMode, setThemeMode] =
        useState<ThemeContextProps['themeMode']>(defaultTheme);
    
      const toggleTheme = useCallback(() => {
        setThemeMode((prevValue) => 
          (prevValue === 'light' ? 'dark' : 'light'));
      }, []);
    
      const providerProps = useMemo(
        () => ({
          themeMode,
          theme: themeMode === 'light' ? lightTheme : darkTheme,
          toggleTheme,
        }),
        [themeMode, toggleTheme],
      );
    
      return (
        <ThemeContext.Provider value={providerProps}>
          <MuiThemeProvider theme={providerProps.theme}>
            <EmotionThemeProvider theme={providerProps.theme}>
              {children}
            </EmotionThemeProvider>
          </MuiThemeProvider>
        </ThemeContext.Provider>
      );
    }