Switch between light and dark themes in antd using React at runtime

Despite finding a similar question on Stack Overflow, the provided answer did not solve my issue. I have attempted to follow the steps mentioned, but unfortunately, there has been no noticeable effect. I am struggling with where to place the config-overrides.js file as indicated in the answer, and it appears that there are still many modules yet to be installed.

In addition, I have also tried following the instructions on the Ant Design website to set up a dark theme for the web application. While I managed to successfully change to a dark theme after starting the application, I am unsure how to select the specific theme (dark or light) that I want at runtime.

Project Structure:

package.json: (changes made on scripts portion)

{
  "name": "testout",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@ant-design/dark-theme": "^2.0.2",
    "@craco/craco": "^5.6.4",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "antd": "^4.6.4",
    "antd-theme": "^0.3.4",
    "craco-less": "^1.17.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.3"
  },
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
   

App.js

import React from 'react';
import './App.less';
import 'antd/dist/antd.less';
import { Button, Select } from 'antd';
function App() {
  const { Option } = Select;

  function handleChange(value) {
    console.log(`selected ${value}`);
  }
  return (
    <div className="App">
      <Button type="primary">Button</Button>
      <Select defaultValue="lucy" style={{ width: 120 }} onChange={handleChange}>
        <Option value="jack">Jack</Option>
        <Option value="lucy">Lucy</Option>
        <Option value="disabled" disabled>
          Disabled
      </Option>
        <Option value="Yiminghe">yiminghe</Option>
      </Select>

    </div>
  );
}

export default App;

craco.config.js

const CracoLessPlugin = require('craco-less');
const { getThemeVariables } = require('antd/dist/theme');


module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars:  getThemeVariables({
                         dark: true, 
                         compact: true,
            }),
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

Output:

Answer №1

After finding the solution through this informative article, I was able to successfully implement a theme switcher for toggling between light and dark themes in my project. The article not only provided detailed instructions on the theme switcher but also demonstrated how to customize the color of antd components.

I have created a demo repository on GitHub showcasing a simplified version that allows switching between black and light themes for illustration purposes.

An antd Select component has been added at the top of the webpage to enable users to select either 'dark' or 'light' theme. Here are snapshots showing the theme switch at runtime:

https://i.stack.imgur.com/LIQOo.png?s=256     https://i.stack.imgur.com/1H8FV.png?s=256

Below are the steps to implement the theme switcher:

  1. Start by installing the necessary packages.

    npm i react-app-rewire-antd-theme antd-theme antd
    
  2. Download the ~public/color.less file from the GitHub repository and place it in your ~public folder.

  3. Copy the code snippets provided into your ~/public/index.html file, usually at the bottom of the <body> tag or after the body content such as <div id="root"></div> for a default React index.html file.

    <link rel="stylesheet/less" type="text/css" href="color.less" />
    <script>
          window.less = {
             async: true,
             env: 'production'
    };
    </script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js"></script>
    
  4. Move the ~src/components/dark.json and ~src/components/light.json files to your desired folder location (available in the ~src/components/ directory on GitHub).

  5. Utilize the provided code snippet in ~src/components/ThemeSelector to toggle between the dark and light themes. Feel free to customize the code according to your requirements for theme switching.

      //sample code
      let vars = value === 'light' ? lightVars : darkVars;
      vars = { ...vars, '@white': '#fff', '@black': '#000' };
      window.less.modifyVars(vars).catch(error => {});
      setTheme(value)
    

Answer №2

Efficient Solution Utilizing React.Lazy

View the Code:

Check out the Stackblitz code reference and demo: https://stackblitz.com/edit/react-6ub59h?file=index.js

  • This approach does not dynamically alter the theme,
  • It maintains user preferences using localStorage. (other methods can be implemented instead of localStorage)
  • The application reloads whenever the user changes the theme

Screenshot of Dark Mode
Screenshot of Light Mode

index.js

import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import { Button, Space } from 'antd';
import "antd/dist/antd.css";
import "./index.css";
import App from "./app";

export const themeConst = {
  DARK: "Dark 🌙",
  LIGHT: "Light ☀️"
};

const LightThemeComponent = React.lazy(() => import("./light"));
const DarkThemeComponent = React.lazy(() => import("./dark"));

const curTheme =
  localStorage.theme == themeConst.DARK ? themeConst.DARK : themeConst.LIGHT;

const toggleTheme = function (isDarkMode) {
  if (curTheme == themeConst.DARK) {
    localStorage.setItem("theme", themeConst.LIGHT);
  } else {
    localStorage.setItem("theme", themeConst.DARK);
  }
  window.location.reload();
};

ReactDOM.render(
  <Space direction="vertical">
    <Button onClick={toggleTheme}>Switch to {(curTheme == themeConst.LIGHT) ? themeConst.DARK : themeConst.LIGHT} Mode</Button>
    {curTheme == themeConst.DARK && (
      <Suspense fallback={<div>Loading...</div>}>
        <DarkThemeComponent>
          <App />
        </DarkThemeComponent>
      </Suspense>
    )}
    {curTheme == themeConst.LIGHT && (
      <Suspense fallback={<div>Loading...</div>}>
        <LightThemeComponent>
          <App />
        </LightThemeComponent>
      </Suspense>
    )}
  </Space>,
  document.getElementById("container")
);

dark.js

import React, { Suspense } from "react";
import "antd/dist/antd.dark.css";

const DarkApp = (props) => {
  return <>{props.children}</>;
};

export default DarkApp;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When React object state remains unchanged, the page does not update automatically

i have a state object with checkboxes: const [checkboxarray_final, setCheckboxarray_final] = useState({ 2: ",4,,5,", 9: ",1,", }); i'm working on enabling check/uncheck functionality for multiple checkboxes: these are ...

Finding the file path to a module in a NextJS application has proven to be a challenge when utilizing the module

Currently, I am utilizing the webpack plugin module-federation/nextjs-mf, which enables us to work with a micro-frontend architecture. Based on the official documentation and referencing this particular example, it is possible to share components between ...

Accessing loop variables in Render and passing them into componentDidMount() in ReactJS to include as a query parameter in an API call

Within the render function, I am using a loop to rotate an array of coordinates in order to position markers on a map. {coords.map(({ lat, lng }, index) => (code goes here and so on))} I intend to replace query parameters with the variable generated f ...

The CSS_MODULES encountered a module build error when utilizing the extract-text-webpack-plugin

While processing CSS with CSS modules in a production environment, I encounter an error, but everything works fine in the development environment. Here is the configuration for webpack.base.js: const path = require("path") const webpack = require("webpac ...

Exploring Array Iteration: Navigating through Arrays with the .map Method in React and Vue

I am currently using Vue after coming from a React background. In React, there is a method called .map that allows you to render a component multiple times based on the number of items in an array and extract data from each index. Here's an example: f ...

I encountered an error when attempting to use Router from next/router

In my latest project using Nextjs and Zustand, I've encountered a problem with next/router. With my current Zustand setup, I'm unable to destructure router from useRouter, so instead, I've resorted to directly importing the Router object in ...

"ReactJS and Express: Building versatile applications for the public and administrative use

Currently, I am in the process of developing a single page application using ReactJS with a separate admin SPA. After going through 4-5 tutorials to establish the basic structure, I find myself at a point where I need guidance on how to create the admin se ...

Error: There was an issue registering the component as the target container is not recognized as a valid DOM element

Upon executing the React code below, I encountered the following error: import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render( <div id="root"> <h1>Hello, world!</h1></div>, document ...

My element is unexpectedly changing its properties

One puzzling issue I'm facing involves a component that retrieves an array from a parent prop, stores it in a state, makes modifications to the array, and then aims to send the modified version back up to the parent. The confusion arises when I obser ...

Retrieve information from SWR mutate following a successful login

I'm currently working on a project that involves a nextJS application with a Laravel backend. I've been experimenting with Laravel-NextJS for this project. So far, all login and backend functions are functioning properly. Here's some code ...

There was a problem with the WebSocket handshake: the response header value for 'Sec-WebSocket-Protocol' did not match any of the values sent

I've encountered an issue with my React project that involves streaming live video through a WebSocket. Whenever the camera firmware is updated, I face an error in establishing the WebSocket connection. Here's how I initiate the WebSocket: wsRe ...

Issue with color attribute not being recognized within Typography component's sx prop

I've run into a problem with the 'sx' property of a Typography component where I can't seem to apply the 'color'. Any insights on what could be causing this issue? https://i.stack.imgur.com/VhWtH.png If I remove the 'co ...

Moving the Google Maps zoom button to the top of the screen

I am looking to adjust the placement of my zoom panel slightly towards the top in the image provided. Any suggestions or guidance would be greatly appreciated. Thank you. <GoogleMapReact bootstrapURLKeys={{ key: `${process.env.REACT_APP_GOOGLE_MAPS_K ...

The correct way to type a generic React function component using TypeScript

When attempting to generalize the function component Element into a GenericElement component in TypeScript Playground, I encountered syntax issues that raised complaints from TypeScript. What is the correct method for typing a generic react function compo ...

Why does the data in useState only update after clicking the button for the second time?

When utilizing useState to set a value, I noticed that it only updates upon clicking the button for the second time. Why does this occur? const DropDownMenu = ({ element, segmentEnd, segmentStart }) => { const [open, setOpen] = React.useState(false); ...

Leveraging NextJS for Advanced Server-Side Rendering with Seamless Integration of React Query in the Front

I have implemented React Query in the following manner: const { data, isSuccess, isLoading } = useQuery(['myQuery', myParams], async () => { return myAjaxCall(myParams); }, { cacheTime: 0 }); The results are then passed into a custom ...

Put emphasis on the input field - React component

My React component features an input field with a disabled attribute. When the component is clicked, the input becomes enabled for the user to type in. I have successfully implemented this functionality so far, but now I need to focus on the input field on ...

"Uh-oh! Encountered a new unexpected runtime error. Can't seem

While working on my portfolio in Next.js, I encountered an issue. I added a header to display on all pages by placing it in _app.js without making any changes to _document.js. Here is the error message: Unhandled Runtime Error Error: No router instance fo ...

JSX: dynamically include element based on condition

I am currently utilizing Bootstrap 3 and I have a requirement to clear each .row once 12 columns have been generated. However, my code is not working as expected and I encounter this error: Failed to compile. Error in ./src/File/file.js Syntax error: Unex ...

To interact with Cognito in a React application, it is essential to provide the necessary Username and Pool

I'm currently working on creating a demo app using Cognito with React. I've been trying out the code available in this GitHub repository to experiment with it. However, I keep encountering the error message Username and Pool information required ...