reactjsbuttonmaterial-uidarkmodelightmode

Dark Mode in react using MUI v5


Trying to create a Toggle to switch from dark mode to light mode has been quite difficult for me in v5.

Using the code directly from MUI Sandbox MUI darkmode, I attempted to separate the code to work in the app.js and my Navbarpractice.js.

App.js

import React from "react";
import useMediaQuery from '@mui/material/useMediaQuery';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { Paper } from "@mui/material";
import BasicCard from "./components /Card.js";
import Navbarpractice from "./components /Navbar/Navbarpractice"

const ColorModeContext = React.createContext({ toggleColorMode: () => {} });


function App() {
  const [mode, setMode] = React.useState('light');
  const colorMode = React.useMemo(
    () => ({
      toggleColorMode: () => {
        setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
      },
    }),
    [],
  );

  const theme = React.useMemo(
    () =>
      createTheme({
        palette: {
          mode,
        },
      }),
    [mode],
  );

  return (
    <ColorModeContext.Provider value={colorMode}>
    <ThemeProvider theme={theme}>
    <div className="App">
    <Navbarpractice/>
      <BasicCard/>
    </div>
    </ThemeProvider>
    </ColorModeContext.Provider>
  );
}

export default App;

Navbarpractice.js

import React from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import MenuIcon from '@mui/icons-material/Menu';
import { useTheme, ThemeProvider, createTheme } from '@mui/material/styles';
import { teal } from '@mui/material/colors';
import { withStyles } from '@mui/styles';
import { Switch } from '@mui/material';
import Brightness4Icon from '@mui/icons-material/Brightness4';
import Brightness7Icon from '@mui/icons-material/Brightness7';


const label = { inputProps: { 'aria-label': 'Switch' } };

const ColorModeContext = React.createContext({ toggleColorMode: () => {} });

const theme = createTheme({
  
    Navbar: {
      primary: {
        // Purple and green play nicely together.
        main: teal[500],
      },
      secondary: {
        // This is green.A700 as hex.
        main: '#11cb5f',
      },
    },
  });

  const TealTextTypography = withStyles({
    root: {
      color: "#008080"
    }
  })(Typography);

function Navbar() {

  const theme = useTheme();
  const colorMode = React.useContext(ColorModeContext);

    return (
      <ColorModeContext.Provider value={colorMode}>
    <ThemeProvider theme={theme}>\
      <Box sx={{ flexGrow: 1 }}>
        <AppBar position="static"
        style={{ background: 'transparent', boxShadow: 'none'}}>
          <Toolbar>
            <IconButton
              size="large"
              edge="start"
            
              aria-label="menu"
              sx={{ mr: 2 }}
            >
              <MenuIcon />
            </IconButton>
            <TealTextTypography variant="h6" component="div" sx={{ flexGrow: 1 }}>
              Mentors
            </TealTextTypography>
            <TealTextTypography variant="h6" component="div" sx={{ flexGrow: 1 }}>
              Mentees
            </TealTextTypography>

            <Box
            sx={{
            display: 'flex',
            width: '100%',
            alignItems: 'center',
            justifyContent: 'center',
            bgcolor: 'background.default',
            color: 'text.primary',
            borderRadius: 1,
            p: 3,
            }}
            >
            <IconButton sx={{ ml: 1 }} onClick={colorMode.toggleColorMode} color="inherit">
            { theme.palette.mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
            </IconButton>

            </Box>
          </Toolbar>
        </AppBar>
      </Box>
      </ThemeProvider>
      </ColorModeContext.Provider>
    );
  }

export default Navbar;

I am certain I am mixing up my const. and placing them in the wrong places. Although I am new to react and Mui I did manage to get it to work statically, however, the toggle is proving to be difficult.


Solution

  • This seem to work for me

    App.js

    import React from 'react';
    import {
      ThemeProvider,
      createTheme,
      responsiveFontSizes,
    } from '@mui/material/styles';
    import { deepmerge } from '@mui/utils';
    import useMediaQuery from '@mui/material/useMediaQuery';
    import { getDesignTokens, getThemedComponents } from 'theme/Theme';
    import { ColorModeContext } from 'config/color-context';
    
    export default function App() {
      const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
      const [mode, setMode] = React.useState();
    
      React.useEffect(() => {
        setMode(prefersDarkMode ? 'dark' : 'light');
      }, [prefersDarkMode]);
    
      const colorMode = React.useMemo(
        () => ({
          toggleColorMode: () => {
            setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
          },
        }),
        []
      );
    
      let theme = React.useMemo(
        () =>
          createTheme(deepmerge(getDesignTokens(mode), getThemedComponents(mode))),
        [mode]
      );
    
      theme = responsiveFontSizes(theme);
    
      return (
        <ColorModeContext.Provider value={colorMode}>
          <ThemeProvider theme={theme}>
           ...         
          </ThemeProvider>
        </ColorModeContext.Provider>
      );
    }
    

    Theme.js

    import { amber, deepOrange, grey, blue, common } from '@mui/material/colors';
    
    const palette = {
      light: {
        primary: {
          main: '#34C0AC',
          light: '#B1DED3',
          dark: '#00765A',
        },
      },
    };
    
    export const getDesignTokens = (mode) => ({
      palette: {
        mode,
        ...(mode === 'light'
          ? {
              primary: {
                main: palette.light.primary.main,
                light: palette.light.primary.light,
                dark: palette.light.primary.dark,
              },
    
              divider: amber[200],
              text: {
                primary: grey[900],
                secondary: grey[800],
              },
            }
          : {
              primary: deepOrange,
              divider: deepOrange[700],
              background: {
                default: deepOrange[900],
                paper: deepOrange[900],
              },
              text: {
                primary: '#fff',
                secondary: grey[500],
              },
            }),
      },
      typography: {
        fontFamily: [
          'Oswald',
          'Roboto',
          '"Helvetica Neue"',
          'Arial',
          'sans-serif',
        ].join(','),
        body1: {
          fontFamily: 'Poppins, Arial, sans-serif',
        },
      },
    });
    
    export const getThemedComponents = (mode) => ({
      components: {
        ...(mode === 'light'
          ? {
              MuiAppBar: {
                styleOverrides: {
                  colorPrimary: {
                    backgroundColor: grey[800],
                  },
                },
              },
              MuiLink: {
                variant: 'h3',
              },
              MuiButton: {
                styleOverrides: {
                  root: {
                    borderRadius: 0,
                    color: common.white,
                    fontFamily:
                      "Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
                    fontSize: 20,
                    borderWidth: 2,
                    '&:hover': {
                      borderWidth: 2,
                    },
                  },
                },
                variants: [
                  {
                    props: { variant: 'contained' },
                    style: {
                      fontFamily:
                        "Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
                    },
                  },
                  {
                    props: { variant: 'outlined' },
                    style: {
                      color: palette.light.primary.main,
                    },
                  },
                  {
                    props: { variant: 'primary', color: 'primary' },
                    style: {
                      border: '4px dashed blue',
                    },
                  },
                ],
              },
              MuiList: {
                styleOverrides: {
                  root: {},
                },
              },
              MuiMenuItem: {
                styleOverrides: {
                  root: {
                    color: common.white,
                    alignItems: 'stretch',
                    fontFamily:
                      "Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
                  },
                },
              },
              MuiAccordion: {
                styleOverrides: {
                  root: {
                    color: common.white,
                    fontFamily:
                      "Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
                  },
                },
              },
            }
          : {
              MuiAppBar: {
                styleOverrides: {
                  colorPrimary: {
                    backgroundColor: blue[800],
                  },
                },
              },
            }),
      },
    });
    

    color-context.js

    import React from 'react';
    
    export const ColorModeContext = React.createContext({
      toggleColorMode: () => {
        // This is intentional
      },
    });
    

    ThemeToggler.js

    import React from 'react';
    import { IconButton, Box } from '@mui/material';
    import { useTheme } from '@mui/material/styles';
    import Brightness4Icon from '@mui/icons-material/Brightness4';
    import Brightness7Icon from '@mui/icons-material/Brightness7';
    import { ColorModeContext } from 'config/color-context';
    
    export default function SubHeaderNavigation() {
      const theme = useTheme();
      const colorMode = React.useContext(ColorModeContext);
    
      return (
        <Box
          sx={{
            display: 'flex',
            width: '100%',
            alignItems: 'center',
            justifyContent: 'center',
            bgcolor: 'background.default',
            color: 'text.primary',
            borderRadius: 1,
            p: 3,
          }}
        >
          {theme.palette.mode} mode
          <IconButton
            sx={{ ml: 1 }}
            onClick={colorMode.toggleColorMode}
            color="inherit"
          >
            {theme.palette.mode === 'dark' ? (
              <Brightness7Icon />
            ) : (
              <Brightness4Icon />
            )}
          </IconButton>
        </Box>
      );
    }