Skip to content
+

Using Joy UI and Material UI together

Learn how to use Joy UI and Material UI together in the same project.

Introduction

There are two main use cases for using them together:

  1. Your existing project already uses Material UI but you're willing to explore the new components and style Joy UI offers.
  2. You've started your project with Joy UI but you find a key component you need is missing.

Additionally, keep these in mind when using them together:

  • Both of them use MUI System as their style engine, which uses React context for theming.
  • Theme scoping must be done on one of the libraries.

Prerequisite

  • Have @mui/material and @mui/joy installed in your project.
  • The version of both libraries must be v5.12.0 or higher.

Set up the providers

Render Joy UI's CssVarsProvider inside Material UI's provider and use THEME_ID to separate the themes from each other.

import {
  experimental_extendTheme as materialExtendTheme,
  Experimental_CssVarsProvider as MaterialCssVarsProvider,
  THEME_ID as MATERIAL_THEME_ID,
} from '@mui/material/styles';
import { CssVarsProvider as JoyCssVarsProvider } from '@mui/joy/styles';
import CssBaseline from '@mui/material/CssBaseline';

const materialTheme = materialExtendTheme();

export default function App() {
  return (
    <MaterialCssVarsProvider theme={{ [MATERIAL_THEME_ID]: materialTheme }}>
      <JoyCssVarsProvider>
        <CssBaseline enableColorScheme />
        ...Material UI and Joy UI components
      </JoyCssVarsProvider>
    </MaterialCssVarsProvider>
  );
}

Sync the color mode

To sync the color mode between the providers, call setMode from both of the libraries:

import { useColorScheme as useJoyColorScheme } from '@mui/joy/styles';
import { useColorScheme as useMaterialColorScheme } from '@mui/material/styles';

const ModeToggle = () => {
  const { mode, setMode: setMaterialMode } = useMaterialColorScheme();
  const { setMode: setJoyMode } = useJoyColorScheme();
  const [mounted, setMounted] = React.useState(false);
  React.useEffect(() => {
    setMounted(true);
  }, []);
  if (!mounted) {
    // prevent server-side rendering mismatch
    // because `mode` is undefined on the server.
    return null;
  }
  return (
    <IconButton
      onClick={() => {
        setMaterialMode(mode === 'dark' ? 'light' : 'dark');
        setJoyMode(mode === 'dark' ? 'light' : 'dark');
      }}
    >
      {/** You can use `mode` from Joy UI or Material UI since they are synced **/}
      {mode === 'dark' ? <DarkMode /> : <LightMode />}
    </IconButton>
  );
};

Default mode

If you want to change the defaultMode, you have to specify the prop to both of the providers:

<MaterialCssVarsProvider
  defaultMode="system"
  theme={{ [MATERIAL_THEME_ID]: materialTheme }}
>
  <JoyCssVarsProvider defaultMode="system">
    <CssBaseline enableColorScheme />
    ...Material UI and Joy UI components
  </JoyCssVarsProvider>
</MaterialCssVarsProvider>

Caveat

Both libraries have the same class name prefix:

import MaterialTypography, {
  typographyClasses as materialTypographyClasses,
} from '@mui/material/Typography';
import JoyTypography, {
  typographyClasses as joyTyographyClasses,
} from '@mui/joy/Typography';
import Stack from '@mui/material/Stack';

<Stack
  sx={{
    // similar to `& .${joyTyographyClasses.root}`
    [`& .${materialTypographyClasses.root}`]: {
      color: 'red',
    },
  }}
>
  {/* Both components are red. */}
  <MaterialTypography>Red</MaterialTypography>
  <JoyTypography>Red</JoyTypography>
</Stack>;

Joy UI and Material UI components have a different name for theming the components. For example, Joy UI's Button uses JoyButton whereas Material UI's Button uses MuiButton.

Edit this page

Was this page helpful?


Next.js App RouterUsing other icon libraries

Blog

Store