Frontend Patterns

Pattern

Theme Provider

Centralize design tokens and enable runtime theme switching across an application.

Styling Beginner

Theme Provider

Problem

Each component fetches and manages its own theme values, leading to duplicated theme logic and inconsistent colors when some components miss theme updates. Implementing dark mode or allowing users to switch themes requires modifying every component individually, and there’s no single source of truth for the current theme state.

Solution

Supply theme values through context so child components can access design tokens consistently. This centralizes theming and makes it easy to switch themes dynamically.

Example

This demonstrates centralizing theme management to provide consistent design tokens across all components and enable runtime theme switching like dark mode.

// Framework-agnostic theme management with CSS custom properties
class ThemeProvider {
  constructor() {
    this.theme = 'light';      // Current theme
    this.listeners = [];       // Components listening for changes
  }

  setTheme(newTheme) {
    this.theme = newTheme;
    // Update data attribute - CSS can react to this
    document.documentElement.setAttribute('data-theme', newTheme);
    // Notify all subscribed components
    this.listeners.forEach(listener => listener(newTheme));
  }

  subscribe(listener) {
    this.listeners.push(listener);
    // Return unsubscribe function
    return () => {
      const index = this.listeners.indexOf(listener);
      this.listeners.splice(index, 1);
    };
  }
}

// Usage
const themeProvider = new ThemeProvider();

// Components can listen for theme changes
themeProvider.subscribe(theme => {
  console.log('Theme changed to:', theme);
});

// Switch themes at runtime
themeProvider.setTheme('dark');

Benefits

  • Centralizes theme management with single source of truth.
  • Makes adding features like dark mode straightforward.
  • Ensures consistent theming across all components.
  • Enables runtime theme switching without page reloads.

Tradeoffs

  • Can cause re-renders when theme changes affect entire component tree.
  • May need optimization to prevent unnecessary re-renders.
  • Requires components to consume theme context instead of hardcoding values.
  • Adds complexity compared to simple CSS custom properties.
Stay Updated

Get New Patterns
in Your Inbox

Join thousands of developers receiving weekly insights on frontend architecture patterns

No spam. Unsubscribe anytime.