Implementing Dark mode for React app with MUI

Sachin Kanishka
3 min readSep 24, 2023

In modern web applications, offering users a choice between light and dark themes is common. In this article, I explain how to use MUI combined with React + Typescript to implement a theme toggle feature for your React application. You can use this project as a starter project, too.

Prerequisites

You should have a basic knowledge of React and how React Context works.

Let's build it together!

First, start your React project. I'm using Vite for installing React with Typescript.

npm create vite@latest YOUR_APP_NAME --template react-ts

Then, install MUI for theming and UI components. Further, Material Icons since we use a couple of icons for this app.

npm install @mui/material @emotion/react @emotion/styled @mui/icons-material

Create your ThemeContextProvider.tsx file in the srcfolder.

Here, we're creating and exporting a new context for other components to access theme-related functionalities. We initialize our theme mode state to "light" by default. This state determines whether the user sees the light or dark theme. With the help of createTheme we define our MUI theme, setting the palette's mode based on our mode state.

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

type ThemeContextType = {
switchColorMode: () => void;
};

type ThemeProviderProps = {
children: ReactNode;
};

export const ThemeContext = createContext<ThemeContextType>({
switchColorMode: () => {},
});

export function ThemeContextProvider({ children }: ThemeProviderProps) {
const [mode, setMode] = useState<"light" | "dark">("light");

const switchColorMode = () => {
setMode((prevMode) => (prevMode === "light" ? "dark" : "light"));
};

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

return (
<StyledEngineProvider injectFirst>
<ThemeContext.Provider value={{ switchColorMode }}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</ThemeContext.Provider>
</StyledEngineProvider>
);
}

In your App.tsx file, define your top bar (AppBar) with an icon button on the right that switches between light and dark modes when clicked. We use the useTheme hook to get the current theme properties and the useContext hook to extract the switchColorMode function from our ThemeContext.

import { DarkModeOutlined, LightModeOutlined } from "@mui/icons-material";
import {
AppBar,
Box,
Container,
IconButton,
Toolbar,
Tooltip,
useTheme,
} from "@mui/material";
import { useContext, useMemo } from "react";
import { ThemeContext } from "./theme";

function App() {
const theme = useTheme();
const { switchColorMode } = useContext(ThemeContext);
const activateName = useMemo(
() => (theme.palette.mode === "dark" ? "Light" : "Dark"),
[theme]
);

return (
<>
<Container
maxWidth={false}
sx={{
padding: "0px !important",
}}
>
<AppBar
position="static"
sx={{
padding: "0px !important",
bgcolor: theme.palette.background.default,
}}
>
<Toolbar>
<Box flex={1}></Box>
<Box sx={{ flexGrow: 0 }}>
<Tooltip title={`Activate ${activateName} Mode`}>
<IconButton
onClick={switchColorMode}
sx={{
p: 1,
border: `1px ${theme.palette.text.disabled} solid`,
}}
size="large"
color="inherit"
>
{theme.palette.mode === "dark" ? (
<LightModeOutlined />
) : (
<DarkModeOutlined color="action" />
)}
</IconButton>
</Tooltip>
</Box>
</Toolbar>
</AppBar>
</Container>
</>
);
}

export default App;

Since this is a starter project, I use only the top bar to show the dark and light mode themes. Later, you can add more components and play with the dark and light modes.

Remember to update your main.tsx file. Here, I import ThemeContextProvider and wrap the App component.

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { ThemeContextProvider } from "./theme";

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ThemeContextProvider>
<App />
</ThemeContextProvider>
</React.StrictMode>
);

Below is the outcome of the above code. The Tooltip component adds some user-friendly context, showing a message when a user hovers over the button, informing them of its function.

Here's the GitHub repository for the complete code. Give it a star ⭐️ if you find it useful.

I'll see you guys in my next post. Until then, happy coding! 👨‍💻

--

--