eMoosavi
Frontend dev braindumps
Creating Dynamic Theming in React with CSS Variables
React Components

Creating Dynamic Theming in React with CSS Variables

Mastering variable-based theming to enhance user experience.

Oct 10, 2024 - 10:375 min read

Dynamic theming is increasingly becoming a sought-after feature in web applications, allowing users to personalize their experience by applying custom styles without the need for full-page reloads. This post explores how to implement dynamic theming in a React application using CSS variables and hooks, along with best practices and optimizations to ensure a seamless user experience.


Understanding CSS Variables

CSS variables, also known as custom properties, allow us to define a variable within CSS that we can reuse throughout our stylesheets. Here’s a basic outline of how a CSS variable is declared:

:root {
  --main-bg-color: coral;
  --main-text-color: white;
}

In this example, we have defined two CSS variables, --main-bg-color and --main-text-color. By utilizing CSS variables, we can change these values dynamically with JavaScript.

Theming Strategy

Before jumping into the implementation, let’s outline a basic theming strategy:

  1. Define a set of themes with variables for colors, fonts, etc.
  2. Manage the current theme in the React state.
  3. Update CSS variables based on the current theme dynamically.
  4. Persist user preferences across sessions using localStorage.

Configuration of Themes

First, let’s define two themes - a light theme and a dark theme:

const themes = {
  light: {
    '--main-bg-color': 'white',
    '--main-text-color': 'black',
  },
  dark: {
    '--main-bg-color': 'black',
    '--main-text-color': 'white',
  }
};

Here we define the themes object, where each key corresponds to a theme, and its value is another object containing the relevant CSS variables.

Next, we need to create a custom hook to manage the theming Logic:

import { useEffect, useState } from 'react';

function useTheme() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    const storedTheme = window.localStorage.getItem('theme');
    if (storedTheme) {
      setTheme(storedTheme);
    }
  }, []);

  useEffect(() => {
    Object.entries(themes[theme]).forEach(([key, value]) => {
      document.documentElement.style.setProperty(key, value);
    });
    window.localStorage.setItem('theme', theme);
  }, [theme]);

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return { theme, toggleTheme };
}

In this hook, we do the following:

  • Initialize the theme state.
  • Check for a stored theme in localStorage upon mounting and set it if exists.
  • Dynamically set the corresponding CSS variables on the :root using setProperty whenever the theme changes.
  • A function, toggleTheme, that changes the theme between light and dark.

This setup allows us to toggle between two themes seamlessly while keeping track of the user's preference.

Using the Theme Hook in a Component

Now let’s use our useTheme hook in a sample component:

import React from 'react';
import useTheme from './useTheme';

function App() {
  const { theme, toggleTheme } = useTheme();

  return (
    <div>
      <h1>Hello, Theme!</h1>
      <button onClick={toggleTheme}>Switch to {theme === 'light' ? 'Dark' : 'Light'} Theme</button>
    </div>
  );
}

This component pulls in the current theme and the toggleTheme function from the useTheme hook. The button allows users to switch themes, and the text updates accordingly.

Optimizing Performance

In larger applications, changing themes might involve more than just a couple of variables. You could optimize performance further by considering the following:

  • Selective rendering of components affected by theme changes.
  • CSS transition effects for smoother visual changes.

You can also leverage libraries such as styled-components or emotion for more dynamic theming capabilities that provide scoped theming without the need for manual CSS variable management.

Advanced Theming with React Context

As you scale your application, your theming strategy might require a more advanced architecture. At this stage, you might want to leverage the Context API to provide theme data down through your component tree without prop drilling.

Here’s a brief outline of how to set up a ThemeContext:

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const { theme, toggleTheme } = useTheme();
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useThemeContext = () => useContext(ThemeContext);

This allows any component within your application to access theme data easily:

import { useThemeContext } from './ThemeProvider';

function ThemedComponent() {
  const { theme, toggleTheme } = useThemeContext();
  // Component implementation here...
}

Conclusion

Dynamic theming can significantly enhance the user experience in your applications, allowing personalization and accessibility. Using CSS variables in conjunction with React hooks provides an elegant and powerful way to manage multiple themes. The user-friendly API we’ve created is both efficient and easy to implement, making it an excellent addition to our web development arsenal.

By following the outlined approaches and best practices, you can create React applications that not only look good but also provide your users with a customized experience tailored to their preferences. Experiment with other theme variations, customize your designs, and enjoy the changing visual landscape that dynamic theming provides!

Feel free to build upon this basic implementation and expand into more complex themes and styles as your application grows.

Article tags
themabledynamic-themingcss-variablesreact-hooksfrontend-architecture
Previous article

Web Performance

Efficient Memoization Patterns in React