DEV Community

Cover image for ๐ŸŒž Toggle theme ๐ŸŒ‘ in React with MUI v5 and Redux ๐Ÿš€
Rajesh Joshi
Rajesh Joshi

Posted on

๐ŸŒž Toggle theme ๐ŸŒ‘ in React with MUI v5 and Redux ๐Ÿš€

Dark and Light theme in any Web-App make it even more attractive. To build a theme enabled web-app in ReactJS with the help of Material-UI v5 and Redux follow this article.


Theme Animation


Directory Structure

src
โ”‚   App.js
โ”‚   index.css
โ”‚   index.js
โ”‚
โ””โ”€โ”€โ”€theme
โ”‚   โ”‚   theme.js
โ”‚
โ””โ”€โ”€โ”€redux
โ”‚   โ”‚   store.js
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€theme
โ”‚       โ”‚   themeSlice.js
Enter fullscreen mode Exit fullscreen mode

Create a React Project

Using npx create a new react project.

$ npx create-react-app my-site
Enter fullscreen mode Exit fullscreen mode

Add dependencies

Add the following libraries

$ yarn add @reduxjs/toolkit react-redux @mui/material @emotion/react @emotion/styled
Enter fullscreen mode Exit fullscreen mode

your package.json should look like this

{
  "name": "my-site",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.7.1",
    "@emotion/styled": "^11.6.0",
    "@mui/material": "^5.2.7",
    "@reduxjs/toolkit": "^1.7.1",
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^12.1.2",
    "@testing-library/user-event": "^13.5.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.6",
    "react-scripts": "5.0.0",
    "web-vitals": "^2.1.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": ["react-app", "react-app/jest"]
  },
  "browserslist": {
    "production": [">0.2%", "not dead", "not op_mini all"],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's Write Code ๐Ÿš€

Start with Writing Store / Redux

To learn the basics of the new Redux using a simple Counter App, you may have a look at my recent article New Redux ๐Ÿ˜ฑ is just ๐Ÿ”ฅ.

Create themeSlice.js

import { createSlice } from "@reduxjs/toolkit";

export const themeSlice = createSlice({
  name: "theme",
  initialState: {
    darkTheme: false,
  },
  reducers: {
    toggleTheme: (state) => {
      state.darkTheme = !state.darkTheme;
    },
  },
});

export const { toggleTheme } = themeSlice.actions;

export default themeSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

In initialState the default value of darkTheme is false, i.e. by default the theme will be light.

Register the themeSlice in store. So, your store.js should look like this.

import { configureStore } from "@reduxjs/toolkit";
import theme from "./theme/themeSlice";

export default configureStore({
  reducer: {
    theme,
  },
});
Enter fullscreen mode Exit fullscreen mode

Don't forget to provide the store to the App in index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";

// redux
import store from "./redux/store";
import { Provider } from "react-redux";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

Let's write theme/theme.js

Define both light and dark themes in theme.js

import { createTheme } from "@mui/material";

export const lightTheme = createTheme({
  palette: {
    mode: "light",
    background: {
      paper: "#f2f2f2",
    },
    text: {
      primary: "#11111",
    },
  },
});

export const darkTheme = createTheme({
  palette: {
    mode: "dark",
    background: {
      paper: "#222",
    },
    text: {
      primary: "#fff",
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Write App.js

Based on the Global state variable darkTheme, provide theme to ThemeProvider HOC.

import { useSelector, useDispatch } from "react-redux";
import { ThemeProvider } from "@mui/material/styles";
import {
  Paper,
  FormGroup,
  FormControlLabel,
  Switch,
  Typography,
} from "@mui/material";

import { darkTheme, lightTheme } from "./theme/theme";
import { toggleTheme } from "./redux/theme/themeSlice";

export default function App() {
  // get theme from store
  const theme = useSelector((state) => state.theme);

  // initialize dispatch variable
  const dispatch = useDispatch();

  // ToggleSwitch component
  const ToggleSwitch = () => {
    return (
      <div
        style={{
          position: "absolute",
          top: "10px",
          right: "10px",
        }}
      >
        <FormGroup>
          <FormControlLabel
            control={
              <Switch
                checked={theme.darkTheme}
                onChange={() => dispatch(toggleTheme())}
              />
            }
            label="Toggle Theme"
          />
        </FormGroup>
      </div>
    );
  };

  return (
    <ThemeProvider theme={theme.darkTheme ? darkTheme : lightTheme}>
      <Paper
        style={{
          minHeight: "100vh",
          borderRadius: "0",
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <ToggleSwitch />
        <Typography variant="h1">Hello</Typography>
      </Paper>
    </ThemeProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Run the Development Server ๐Ÿš€

$ yarn start
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:3000

Light Theme

Light Theme

Dark Theme

Dark Theme


Hurray! You just learned ๐ŸŒž Toggle theme ๐ŸŒ‘ in React with MUI v5 and Redux ๐Ÿš€


I hope, you guys liked this quick tutorial. If so, then please don't forget to drop a Like โค๏ธ

And also, help me reach 1k Subscribers ๐Ÿคฉ, on my YouTube channel.

Happy Coding! ๐Ÿ˜ƒ๐Ÿ’ป

Discussion (7)

Collapse
alieslam profile image
Ali Eslam

Thank you!

Collapse
raibtoffoletto profile image
Raรญ B. Toffoletto

A custom hook useTheme would be probably easier ๐Ÿ˜‰

Collapse
timrohrer profile image
tim.rohrer

Do you have a link to an article describing this approach?

Collapse
raibtoffoletto profile image
Raรญ B. Toffoletto

Not from the top of my head, but look for hooks and context.
The idea is to create a provider that will create the theme and hold the state with a method for toggling it. with that like redux, it can be called from any sub component.

For me looks like a cleaner way than setting up a redux store.

Collapse
ashuvssut profile image
Ashutosh Khanduala • Edited on

create your custom useTheme hook: youtu.be/5LrDIWkK_Bc

Thread Thread
timrohrer profile image
tim.rohrer

I'll check it out. Thanks.

Collapse
harshitaarya profile image
Harshita Arya

Thanks, this is really helpful๐Ÿ”ฅ