I am new Docusaurus user, trying to synchronise Docusaurus dark/light mode with MaterialUI's dark/light mode. For example, when the toggle switch is changed from light to dark mode in Docusaurus then dark mode should be activated in MaterialUI.
My approach so far has been to swizzle Docusaurus ColorModeToggle
via
wrapping. From the wrapped ColorModeToggle
I retrieve a function stored in a React context to toggle the light/dark theme in MaterialUI. Within the swizzled Root
I use the react context provider which, in turn, wraps a MaterialUI ThemeProvider
. For further details I have included the code below.
However, when I browse my site, I get the following error:
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Has anyone else managed to synchronise Docusaurus light/dark theme with MaterialUI?
The swizzled ColorModeToggle
import React from "react";
import ColorModeToggle from "@theme-original/ColorModeToggle";
import { useToggleTheme } from "@site/src/components/MuiTheme";
export default function ColorModeToggleWrapper(props) {
console.log("<ColorModeToggleWrapper> properties = " + JSON.stringify(props));
// "value" holds docusaurus color theme. Either "light" or "dark"
const { value } = props;
const muiToggle = useToggleTheme();
console.log("Docusaurus theme = " + value);
console.log("MUI theme dark = " + muiToggle());
return (
<>
<ColorModeToggle {...props} />
</>
);
}
The React Context
import React, { useContext } from "react";
import { createTheme, ThemeProvider } from "@mui/material/styles";
const CustomThemeContext = React.createContext({ toggleTheme: () => {} });
const darkTheme = createTheme({
components: {
MuiListItemText: {
styleOverrides: {
primary: {
color: "orange",
},
secondary: {
color: "purple",
},
},
},
},
palette: {
mode: "dark",
primary: {
main: "hsl(8,71%,28%)" /* burgundy mapped to link */,
},
secondary: {
main: "hsl(61,78%,26%)" /* brown */,
},
},
});
const lightTheme = createTheme({
components: {
MuiListItemText: {
styleOverrides: {
primary: {
color: "aqua",
},
secondary: {
color: "grey",
},
},
},
},
palette: {
mode: "light",
primary: {
main: "hsl(8,10%,18%)" /* burgundy mapped to link */,
},
secondary: {
main: "hsl(61,18%,26%)" /* brown */,
},
},
});
export function CustomThemeProvider({ children }) {
const [dark, setDark] = React.useState(false);
function toggleTheme() {
console.log("toggleTheme :: from dark = " + dark);
if (dark === true) {
setDark(false);
} else {
setDark(true);
}
}
const theme = React.useMemo(() => {
if (dark === true) {
return createTheme(darkTheme);
}
return createTheme(lightTheme);
}, [dark]);
return (
<CustomThemeContext.Provider value={toggleTheme}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</CustomThemeContext.Provider>
);
}
export function useToggleTheme() {
const context = useContext(CustomThemeContext);
if (context === undefined) {
throw new Error(
"useCustomThemeContext must be used within an CustomThemeProvider"
);
}
return context;
}
The Root component
import React from "react";
import { CustomThemeProvider } from "@site/src/components/MuiTheme";
import App from "@site/src/components/App";
export default function Root({ children }) {
return (
<>
<CustomThemeProvider>
<App children={children}></App>
</CustomThemeProvider>
</>
);
}
The App component
import React from "react";
export default function App(props) {
return <React.Fragment>{props.children}</React.Fragment>;
}