skip to Main Content

I have the following file theme.js

I have 3 modes dark, classic and light. And I toggle the modes using the Buttons as mentioned in Home.js file

import { createTheme } from "@mui/material";
import { createContext, useMemo, useState } from "react";

export const tokens = (mode) => ({
  ...(mode === "dark"
    ? {
        // DARK MODE
        primary1: {
          500: "#1E2230",
        },

        primary2: {
          500: "#171A25",
        },

        primary3: {
          500: "#242937",
        },
        button: {
          main: "#3555FF",
        },
      }
    : mode === "classic"
    ? {
        // CLASSIC MODE
        primary1: {
          500: "#211839",
        },
        primary2: {
          500: "#150D29",
        },
        primary3: {
          500: "#211839",
        },
      }
    : // LIGHT MODE
      {
        primary1: {
          100: "#040507",
          200: "#090a0e",
          300: "#0d0e15",
          400: "#12131c",
          500: "#161823",
          600: "#45464f",
          700: "#73747b",
          800: "#a2a3a7",
          900: "#d0d1d3",
        },

        primary2: {
          100: "#06070a",
          200: "#0c0e13",
          300: "#12141d",
          400: "#181b26",
          500: "#1e2230",
          600: "#4b4e59",
          700: "#787a83",
          800: "#a5a7ac",
          900: "#d2d3d6",
        },

        primary3: {
          100: "#07080b",
          200: "#0e1016",
          300: "#161921",
          400: "#1d212c",
          500: "#242937",
          600: "#50545f",
          700: "#7c7f87",
          800: "#a7a9af",
          900: "#d3d4d7",
        },
      }),
});

const DARK_MODE_BACKGROUND = "#131017";
// const CLASSIC_MODE_BACKGROUND = "#16121C";
const CLASSIC_MODE_BACKGROUND = "red";
const LIGHT_MODE_BACKGROUND = "white";

export const themeSettings = (mode) => {
  const colors = tokens(mode);
  return {
    palette: {
      mode: mode,
      ...(mode === "dark"
        ? {
            primary: {
              main: colors.primary1[500],
            },
            secondary: {
              main: colors.primary2[500],
            },
            background: {
              default: DARK_MODE_BACKGROUND,
            },
          }
        : mode === "classic"
        ? {
            primary: {
              main: colors.primary1[500],
            },
            secondary: {
              main: colors.primary2[500],
            },
            background: {
              default: CLASSIC_MODE_BACKGROUND,
            },
          }
        : {
            primary: {
              main: colors.primary1[100],
            },
            secondary: {
              main: colors.primary2[100],
            },
            background: {
              default: LIGHT_MODE_BACKGROUND,
            },
          }),
    },
  };
};

// context for color mode
export const ColorModeContext = createContext({
  toggleColorMode: () => {},
});

export const useMode = () => {
  // set the mode
  const [mode, setMode] = useState("dark");
  const colorMode = useMemo(
    () => ({
      toggleColorMode: (mode) => {
        switch (mode) {
          case "dark":
            setMode("dark");
            break;
          case "classic":
            setMode("classic");
            break;
          case "light":
          default:
            setMode("light");
            break;
        }
      },
    }),
    []
  );

  const theme = useMemo(() => createTheme(themeSettings(mode)), [mode]);
  return [theme, colorMode, setMode];
};

My Issue is that when I click on Dark Button then it toggles to Dark Mode, when I click on Light then it toggles to Light Mode but When I click on Classic Button then the app breaks I don’t understand what am I doing wrong

import { Button, Typography } from "@mui/material";
import React, { useContext } from "react";
import { ColorModeContext, tokens } from "../Theme";
import { useTheme } from "@emotion/react";

export default function Home() {
  const theme = useTheme();
  const colors = tokens(theme.palette.mode);
  const colorMode = useContext(ColorModeContext);

  return (
    <div>
      hi
      <Button
        onClick={() => colorMode.toggleColorMode("dark")}
        variant="contained"
      >
        Dark
      </Button>
      <Button
        onClick={() => colorMode.toggleColorMode("classic")}
        variant="contained"
      >
        Classic
      </Button>
      <Button
        onClick={() => colorMode.toggleColorMode("light")}
        variant="contained"
      >
        Light
      </Button>
      <Typography variant="h1" color="primary"></Typography>
    </div>
  );
}

Where am I going wrong can anyone guide through ?

The error I get in the console is

MUI: The palette mode classic is not supported.

ERROR Cannot read properties of undefined (reading ‘primary’)
TypeError: Cannot read properties of undefined (reading ‘primary’)
at body (http://localhost:8080/main.js:3923:45)
at styles (http://localhost:8080/main.js:3952:8)
at styles (http://localhost:8080/main.js:3981:24)
at GlobalStyles.globalStyles (http://localhost:8080/main.js:5598:69)
at handleInterpolation (http://localhost:8080/main.js:1619:24)
at serializeStyles (http://localhost:8080/main.js:1733:15)
at http://localhost:8080/main.js:1229:87
at http://localhost:8080/main.js:826:12
at renderWithHooks (http://localhost:8080/main.js:32413:18)
at updateForwardRef (http://localhost:8080/main.js:35329:20)

2

Answers


  1. MUI internally only has support for two modes ‘light’ and ‘dark’.

    Material UI comes with two palette modes: light (the default) and dark. [1]

    The error rightly suggests that behavior. "MUI: The palette mode classic is not supported."

    Since you are managing the mode externally by yourself on your ColorModeContext component. You could remove the mode from the palette object itself. Your color generation should work normally, and referencing mode should now be done from useMode hook instead of useTheme.

    export const themeSettings = (mode) => {
      const colors = tokens(mode);
      return {
        palette: {
          mode: mode, // 👈 remove this
          ...(mode === "dark"
            ? {
                primary: {
                  main: colors.primary1[500],
                },
                secondary: {
                  main: colors.primary2[500],
                },
                background: {
                  default: DARK_MODE_BACKGROUND,
                },
              }
            : mode === "classic"
            ? {
                primary: {
                  main: colors.primary1[500],
                },
                secondary: {
                  main: colors.primary2[500],
                },
                background: {
                  default: CLASSIC_MODE_BACKGROUND,
                },
              }
            : {
                primary: {
                  main: colors.primary1[100],
                },
                secondary: {
                  main: colors.primary2[100],
                },
                background: {
                  default: LIGHT_MODE_BACKGROUND,
                },
              }),
        },
      };
    };
    

    After removing that, your way of getting colors should now be done with the useMode hooks instead of useTheme you are using.

    On you useMode() hook you could also pass the current mode state.

    export const useMode = () => {
      ..
      ..
      return { theme, colorMode, setMode, mode }; // 👈 changed to object for easy destructuring
    }
    
    export default function Home() {
      const { theme, colorMode, mode } = useMode();
      const colors = tokens(theme.palette.mode);
    
      return (
        <div>
          hi
          <Button
            onClick={() => colorMode.toggleColorMode("dark")}
            variant="contained"
          >
            Dark
          </Button>
          <Button
            onClick={() => colorMode.toggleColorMode("classic")}
            variant="contained"
          >
            Classic
          </Button>
          <Button
            onClick={() => colorMode.toggleColorMode("light")}
            variant="contained"
          >
            Light
          </Button>
          <Typography variant="h1" color="primary"></Typography>
        </div>
      );
    }
    

    Further note, I think you should

    1. Move the state for useMode to ColorModeContext.Provider‘s value.
    2. Wrap your ColorModeContext.Provider above ThemeProvider
    3. useMode should now just call useContext(ColorModeContext)
    4. Pass the theme you receive from useMode hook to the ThemeProvider MUI provides.

    The logic is fine, its the steps that are all over the place.

    References:

    1. https://mui.com/material-ui/customization/dark-mode/
    Login or Signup to reply.
  2. I am also facing the same issue in past and solution that work for me is to change import from @mui/material to @mui/material/styles. so what you have to do is just change you import from import { createTheme } from @mui/material to import { createTheme } from @mui/material/styles. Reason behind this is by using @mui/material/style it can handle all the issues internally.

    change from

    import { createTheme } from @mui/material;

    to

    import { createTheme } from @mui/material/styles.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search