Guide to adding a theme switcher feature to your current React website

Currently, I am faced with a challenge involving two theme files, theme.js and theme-dark.js, within the context of a complex React-based website. Despite having already set up the site, I am struggling to find a way to allow users to seamlessly switch between these two themes using a switcher integrated into the website.

The following snippet showcases how my index.js render function is structured:

const rootElement = document.getElementById('root')
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
)

Moreover, this excerpt illustrates the relevant code present in the App.tsx file:

<ThemeProvider theme={theme}>
     <CssBaseline />
     <SnackbarProvider
          .....
     </SnackbarProvider>
</ThemeProvider>

Within the App.tsx file, the above code is nested within custom Context provider components responsible for loading the initial components of the site.

Despite providing the existing code snippets above, I have hit a roadblock when attempting to incorporate a functionality allowing the user to toggle between the theme.js and theme-dark.js themes. Any guidance or assistance that can be provided would be profoundly appreciated. Unfortunately, due to security protocols at my company, I am unable to share additional code. However, it appears that the main obstacle lies in the conflict arising when introducing a custom theme provider alongside the <Provider> element in index.js.

Answer №1

To efficiently handle the job, consider implementing a simple state that can be toggled deep within your application where the switch/button is located.

While passing through with Context is an option, leveraging Redux eliminates the need to reinvent the wheel.

Start by creating a reducer for your theme type:

// isDarkModeReducer.js

export default function isDarkModeReducer(state = false, action) {
  switch (action.type) {
    case 'toggleDarkMode': {
      return !state;
    }

   default: 
    return state;
  }
}

Integrate it into your rootReducer:

// rootReducer.js
...
import isDarkModeReducer from '<location of your isDarkModeReducer reducer>';
...
const rootReducer = combineReducers({
  ...
  isDarkMode: isDarkModeReducer
})
...

In your App.tsx, retrieve the isDarkMode value from the store and apply it conditionally to load either theme.js or theme-dark.js:

// App.tsx
...
import theme from 'theme.js';
import theme-dark from 'theme-dark.js';
import { useSelector } from 'react-redux'

const isDarkMode = useSelector(state => state.isDarkMode);
...
return (
  <ThemeProvider theme={isDarkMode ? theme-dark : theme}>
     <CssBaseline />
     <SnackbarProvider
          .....
     </SnackbarProvider>
  </ThemeProvider>
);
...

To enable toggling, simply dispatch the toggleDarkMode action from wherever your switch button is positioned:

// SwitchButton
import { useDispatch } from 'react-redux'

const dispatch = useDispatch();

const toggleTheme = () => {
  dispatch({ type: 'toggleDarkMode' });
};


return (
  <button onClick={toggleTheme}>Switch Theme</button>
);

If you wish to persist the value using localStorage, refer to the instructions provided in the official documentation.

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

Warning issued by npm during compilation: The template string expression is unexpected as there should be no curly braces in the

Getting an npm warning during compiling (Unexpected template string expression no-template-curly-in-string) import React from 'react'; const Card = ({name, email, id }) => { return ( <div className='tc bg-light-green dib b ...

The hover function stops working once the dropdown class has been removed

I am currently experimenting with a bootstrap template. In the navigation bar, there is an option for "Blog" and "Test". For the "Test" button, I decided to remove the li class="dropdown " because I wanted to create a button that changes color on hover si ...

Dynamic Cursor Modification Upon Hovering Over Child Component

I am currently working on a React App that features a unique custom cursor. Instead of just a png changed in CSS, my cursor is a colored circle-div that follows the mouse's movement. Within my App, there are multiple child components (project teasers) ...

Utilizing getStaticProps for Performance Optimization in Next.js

I am currently in the process of setting up a blog using Next.js and GraphCMS. I have an ArticleList component that I want to display on multiple pages, such as my homepage and under each article as a recommendation. Since the article list is sourced from ...

Having Trouble Adding a CSS File to Your Django Project? Can't Seem to Get it

I am feeling exhausted trying to solve this problem and now I am in desperate need of help. I have been attempting to add a main.css file to my Django project so that I can handle the CSS myself. I have watched numerous tutorials, but most have crashed m ...

mapStateToProps was invoked, however componentDidUpdate did not trigger

Working on fetching data for GameChart from an API and updating the Redux state. In my GameChart.jsx file, I have a chart that gets rendered when componentDidUpdate is called. However, there are times when changing the Redux state does not trigger componen ...

Is the background image viewport centered?

I am embarking on a new venture with a website design that is unfamiliar to me, but I have a clear goal in mind... My design concept involves a single large image measuring 1000px x 1000px as the background, while the actual content fills up only 500px x ...

Utilizing Media queries in Material UI makestyles for responsive design

Could really use some assistance with CSS styling I've been attempting to adjust the height of specific cards based on screen width changes. let mainContainerHeight = window.innerWidth < 750 ? "50vh" : "80vh" The issue I' ...

What is the best way to retrieve data from app.post within react.js?

//server.js app.post('/trip', function(req,res){ var params = "something"; getResult(params).then((db)=>{ // I am trying to access the variable called "db" in my App.js(React) file, but I am unsure of how to do so. res.s ...

Highlighted option selection in Material UI dropdown using Cypress

Can someone explain how to select Material-UI dropdown options using Cypress? I'm looking for a simple explanation, thanks! ...

Slide the next section over the current section using full-page JavaScript

I'm currently developing a website utilizing the FullPage.JS script found at this link . My goal is to have the next section slide over the previous one, similar to what is demonstrated in this example. I've attempted setting the position to fix ...

To change the font color to red when clicked, I must create a button using HTML, CSS, and Javascript

Currently, I am utilizing CodePen to assess my skills in creating a website. Specifically, I am focusing on the HTML component. My goal is to change the font color to blue for the phrase "Today is a beautiful sunny day!" Here is the snippet of code that I ...

How can we enforce that only a certain type of ReactElement is allowed to be passed as props to a Component in TypeScript?

eslint and vscode seem to have trouble detecting validation errors when passing incompatible ReactElement types. Despite searching through documentation and examples, I haven't been able to find a solution that works. // Footer.tsx export interface ...

Generate a user with unique UUID identifier and an array of nested objects

When using GraphQL, React, and Prisma, I encountered a challenge in creating a user with a UUID as the ID and a nested object array containing all input data related to that user. My objective is to display all input data associated with a user in React. H ...

CSS: Creating a block that stretches the entire width of its parent block

I've been facing the same issue multiple times. Whenever I use the float property to display one block, the next block ends up overlapping with the first one. For example: Consider the following block of code: .row_item{ width: 30%; display: block; ...

Issue with ReactTS Route Triggering Invalid Hook Call

My implementation of the PrivateRoute component is as follows: interface Props { path: string, exact: boolean, component: React.FC<any>; } const PrivateRoute: React.FC<Props> = ({ component, path, exact }) => { return ( ...

Placing text alongside an image at the top of the page

Here's the HTML code generated by JSF: <div align="center"> <img src="images/background_image.jpg" height="200px" width="30%" style="vertical-align: top"/> <span style=""> Welcome abc, <input type= ...

Retrieve all references to child elements in React

I am working on a component that renders dynamic children, and I need to assign a unique ref to each child (e.g. ref={'childID' + index}) After the children have been loaded, I am looking for a way to loop through them and access their refs. Is ...

Do we need to invoke the unmount function after every test case in React testing library?

describe('<CustomTooltip />', () => { it('should display the tooltip text', async () => { const { container, unmount } = render(<CustomTooltip text='Tooltip Text' />) userEvent.hover(container.quer ...

What are the benefits of incorporating CSS into a CSS block rather than utilizing inline output with HtmlHelper in CakePHP?

Just a few days ago, I embarked on the journey of learning CakePHP through their blog tutorial. Now, I am diving into writing my own small project to gain hands-on experience with the framework. After going through their documentation, I discovered two ...