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' },
};
- 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>
);
};
- 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;
- 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')
);
- 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
πΉ 7. Advantages of Dynamic Multi-Brand Theming β‘
- Centralized theme management for multiple brands.
- Supports runtime theme fetching and updates.
- Components remain generic and brand-agnostic.
- Scalable for future brands without changing component logic.
- Integrates seamlessly with CSS variables, styled-components, or TailwindCSS.
πΉ 8. Story Flow π
- App loads β
ThemeProvider
fetches and applies theme for default brand. - User selects a different brand β
setBrandTheme
updates context. - CSS variables are updated β components re-render with new styles.
- 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)