DEV Community

Cover image for Dynamic Theming in React Using Context API: Multi-Brand 🎨
Yogesh Bamanier
Yogesh Bamanier

Posted on

Dynamic Theming in React Using Context API: Multi-Brand 🎨

Multi-Brand Dynamic Theming in React Using Context API 🎨

While working on a project at my company, I faced a real challenge: we needed to ship the same application to multiple brands, each with its own unique theme and design guidelines. Passing props for every component became messy, and maintaining different codebases for each brand was unsustainable. This is when I explored React Context API for dynamic theming at runtime, and it transformed our workflow.


πŸ”Ή 1. Why Multi-Brand Theming?

  • Avoids prop drilling across components
  • Supports dynamic runtime theme switching
  • Centralizes theme management for consistency
  • Makes the app scalable for multiple clients or brands

Imagine shipping the same project to 5 different brands. Each brand has its own primary and secondary colors, fonts, and styling. You don’t want to rewrite components every time a new brand comes in. Context API solves this elegantly.


πŸ”Ή 2. Define Brand Themes πŸ—οΈ

const defaultThemes = {
  brandA: { primary: '#1E90FF', secondary: '#F0F8FF', text: '#000' },
  brandB: { primary: '#FF6347', secondary: '#FFF0F5', text: '#222' },
  brandC: { primary: '#32CD32', secondary: '#F5FFFA', text: '#111' },
};
Enter fullscreen mode Exit fullscreen mode
  • Each theme contains colors, fonts, and other design tokens.
  • Can easily extend with additional properties such as button styles, border-radius, etc.

πŸ”Ή 3. Dynamic Theme Context Provider 🌍

import React, { createContext, useState, useEffect } from 'react';

export const ThemeContext = createContext();

export const ThemeProvider = ({ children, initialBrand = 'brandA', fetchTheme }) => {
  const [currentBrand, setCurrentBrand] = useState(initialBrand);
  const [theme, setTheme] = useState({});

  useEffect(() => {
    async function loadTheme() {
      const fetchedTheme = fetchTheme ? await fetchTheme(currentBrand) : defaultThemes[currentBrand];
      setTheme(fetchedTheme);
      const root = document.documentElement;
      Object.entries(fetchedTheme).forEach(([key, value]) => {
        root.style.setProperty(`--${key}-color`, value);
      });
    }
    loadTheme();
  }, [currentBrand, fetchTheme]);

  const setBrandTheme = (brand) => setCurrentBrand(brand);

  return (
    <ThemeContext.Provider value={{ currentBrand, setBrandTheme, theme }}>
      {children}
    </ThemeContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • Supports fetching theme data at runtime via an API or local function.
  • Applies CSS variables globally for seamless styling across components.

πŸ”Ή 4. Using Themes in Components 🌈

import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

const Button = () => {
  const { currentBrand, setBrandTheme } = useContext(ThemeContext);

  const handleBrandChange = (e) => setBrandTheme(e.target.value);

  return (
    <div>
      <button style={{ backgroundColor: 'var(--primary-color)', color: 'var(--text-color)', padding: '10px 20px', border: 'none', borderRadius: '5px' }}>
        Click Me
      </button>

      <select value={currentBrand} onChange={handleBrandChange} style={{ marginLeft: '10px' }}>
        <option value="brandA">Brand A</option>
        <option value="brandB">Brand B</option>
        <option value="brandC">Brand C</option>
      </select>
    </div>
  );
};

export default Button;
Enter fullscreen mode Exit fullscreen mode
  • Components automatically adapt to the active brand theme.
  • Dropdown selection allows runtime switching.

πŸ”Ή 5. Wrapping the App 🌍

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ThemeProvider } from './ThemeContext';

const fetchTheme = async (brand) => {
  const response = await fetch(`/themes/${brand}.json`);
  return response.json();
};

ReactDOM.render(
  <ThemeProvider initialBrand="brandA" fetchTheme={fetchTheme}>
    <App />
  </ThemeProvider>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode
  • Themes can be fetched from a remote server or CDN.
  • Supports dynamic updates without redeploying the app.

πŸ”Ή 6. Conceptual Flowchart πŸ—ΊοΈ

User loads app --> ThemeProvider sets initial brand
          |--> fetchTheme() retrieves brand theme (local or API)
          |--> CSS variables updated globally
          |--> Components consume theme via useContext
          |--> User switches brand --> setBrandTheme updates context
          |--> fetchTheme() loads new brand theme
          |--> UI updates dynamically
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή 7. Advantages of Dynamic Multi-Brand Theming ⚑

  1. Centralized theme management for multiple brands.
  2. Supports runtime theme fetching and updates.
  3. Components remain generic and brand-agnostic.
  4. Scalable for future brands without changing component logic.
  5. Integrates seamlessly with CSS variables, styled-components, or TailwindCSS.

πŸ”Ή 8. Story Flow 🌟

  1. App loads β†’ ThemeProvider fetches and applies theme for default brand.
  2. User selects a different brand β†’ setBrandTheme updates context.
  3. CSS variables are updated β†’ components re-render with new styles.
  4. New themes can be fetched dynamically β†’ UI reflects new brand instantly.

Lesson learned: A flexible, runtime-driven theming system saves time, reduces code duplication, and allows shipping the same app to multiple clients with minimal effort.


✍️ Written by Yogesh Bamanier
πŸ”— LinkedIn

tags: react, contextapi, theming, multibrand, dynamictheming, javascript, frontend, webdevelopment

Top comments (0)