skip to Main Content

I’m stuck creating a dark mode toggle feature for my web app. I’m using NextJS with Material UI.
I’ve tried to create a React context which contains a useState and have a switch component change the context.

theme.tsx

This is where I define my theme which is fed into the _app.js file.

// const { darkMode }: ColorModeContextType = useColorModeContext()
// This line doesn't work since technically `theme.tsx` is outside of the React context I defined.  

const muiTheme: Theme = createTheme()

export const baseTheme: Theme = createTheme({
  palette: { mode: darkMode ? "dark" : "light" },
...

colorMode.tsx

I define my React context which is used in the _app.js file as <ColorModeContext.Provider>...</ColorModeContext.Provider>

... 
const ColorModeContext: Context<ColorModeContextType> = createContext({
  darkMode: false,
  setDarkMode: (darkMode: boolean) => {},
})

export const useColorModeContext = () => {
  return useContext(ColorModeContext)
}

interface ColorModeContextProps {
  children: ReactNode
}

export function ColorModeProvider({ children }: ColorModeContextProps) {
  const [darkMode, setDarkMode] = useState<boolean>(false)
  const colorModeContext = useMemo(
    () => ({ darkMode, setDarkMode }),
    [darkMode]
  )

  return (
    <ColorModeContext.Provider value={colorModeContext}>
      {children}
    </ColorModeContext.Provider>
  )
}

appbar.tsx

Where the user can switch dark mode on or off.

...
const { darkMode, setDarkMode }: ColorModeContextType = useColorModeContext()  
const handleDarkModeSelect = (
    _event: ChangeEvent<HTMLInputElement>,
    value: boolean
  ) => {
    setDarkMode(value)
  }

return (
  <CustomDarkModeSwitch
    defaultChecked={false}
    onChange={(event: ChangeEvent<HTMLInputElement>, value: boolean) =>
      handleDarkModeSelect(event, value)
    }
)
/>  

I’ve tried to fit the example at https://mui.com/material-ui/customization/dark-mode to my use case but I’m struggling.

My problem in my case is the theme.jsx file does not recognize the context so I can’t have the context alter the theme. This is the error:

Server Error
TypeError: Cannot read properties of null (reading 'useContext')

2

Answers


  1. Chosen as BEST ANSWER

    Instead of trying to the ColorModeContext inside the ThemeProvider, I put the ThemeProvider around the ColorModeContext.

    export function ColorModeProvider({ children }: ColorModeContextProps) {
      const [darkMode, setDarkMode] = useState<boolean>(false)
    
      const theme = useMemo(
        () =>
          createTheme({
            palette: { mode: darkMode ? "dark" : "light" },
          }),
        [darkMode]
      )
    
      return (
        <ThemeProvider theme={theme}>
          <ColorModeContext.Provider value={{ darkMode, setDarkMode }}>
            {children}
          </ColorModeContext.Provider>
        </ThemeProvider>
      )
    }
    

  2. you’re trying to use the useContext hook outside of a component. It’s possible that you’re importing the theme.tsx file directly in the top-level scope of your application, which would cause the error since useContext can only be used within a component. Try this:

    import { createTheme, ThemeProvider } from "@material-ui/core/styles";
    import { useColorModeContext } from "./colorMode";
    
    export default function AppWrapper({ children }) {
      const { darkMode } = useColorModeContext();
    
      const muiTheme = createTheme({
        palette: {
          mode: darkMode ? "dark" : "light",
        },
      });
    
      return (
        <ThemeProvider theme={muiTheme}>
          {children}
        </ThemeProvider>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search