Dynamic themes in React: eliminate FOUC and customize palette

Dynamic themes in React, eliminating FOUC, and customizing palettes have been our focus for the last 5 years. In production we encounter typical problems: theme switching causes FOUC (Flash of Unstyled Content), user preferences aren't persisted, and custom palettes require a page reload. On one e-c

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1281
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    977
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1026
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1103
  • image_website-_0.webp
    Website development for Red Pear
    550

Dynamic themes in React, eliminating FOUC, and customizing palettes have been our focus for the last 5 years. In production we encounter typical problems: theme switching causes FOUC (Flash of Unstyled Content), user preferences aren't persisted, and custom palettes require a page reload. On one e-commerce project we noticed that 15% of users switch themes, and FOUC led to a conversion drop of up to 3%. We solve these problems architecturally — through CSS Custom Properties, an inline script to eliminate FOUC, and React Context for state management. This approach guarantees instant switching without flicker, respects the system prefers-color-scheme setting, and allows expanding to dozens of themes. Moreover, using CSS variables is tens of times faster than class swapping because the browser doesn't need to recalculate all styles.

Eliminating FOUC with dynamic themes

The proper foundation is CSS Custom Properties. The entire palette and typography are described through variables. Components use only variables — no hardcoded HEX values.

/* Base theme (light) */ :root { --color-bg-primary: #ffffff; --color-bg-secondary: #f5f5f5; --color-text-primary: #1a1a1a; --color-text-muted: #6b7280; --color-accent: #3b82f6; --color-border: #e5e7eb; --shadow-card: 0 1px 3px rgba(0,0,0,0.1); } /* Dark theme */ [data-theme="dark"] { --color-bg-primary: #0f172a; --color-bg-secondary: #1e293b; --color-text-primary: #f1f5f9; --color-text-muted: #94a3b8; --color-accent: #60a5fa; --color-border: #334155; --shadow-card: 0 1px 3px rgba(0,0,0,0.5); } /* Third theme (high contrast) */ [data-theme="high-contrast"] { --color-bg-primary: #000000; --color-text-primary: #ffffff; --color-accent: #ffff00; --color-border: #ffffff; } 

Switching theme is one setAttribute. The transition is instant, without reload.

Inline script against FOUC

The main issue is flickering on load. The solution is an inline script in <head> that executes before any CSS renders and sets the theme.

<script> (function() { var theme = localStorage.getItem('theme'); var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; var resolved = theme || (prefersDark ? 'dark' : 'light'); document.documentElement.setAttribute('data-theme', resolved); })(); </script> 

This script is synchronous and tiny — about 200 bytes. Our experience shows that this approach completely eliminates FOUC while preserving user choice.

State management via React Context

React Context simplifies theme management across the entire application. We use TypeScript to guarantee type safety.

type Theme = 'light' | 'dark' | 'high-contrast' | 'system'; interface ThemeContextValue { theme: Theme; resolvedTheme: 'light' | 'dark' | 'high-contrast'; setTheme: (theme: Theme) => void; } const ThemeContext = createContext<ThemeContextValue | null>(null); export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [theme, setThemeState] = useState<Theme>(() => { if (typeof window === 'undefined') return 'system'; return (localStorage.getItem('theme') as Theme) || 'system'; }); const systemTheme = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light'; const resolvedTheme = theme === 'system' ? systemTheme : theme; useEffect(() => { document.documentElement.setAttribute('data-theme', resolvedTheme); }, [resolvedTheme]); const setTheme = (newTheme: Theme) => { setThemeState(newTheme); localStorage.setItem('theme', newTheme); }; return ( <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}> {children} </ThemeContext.Provider> ); }; export const useTheme = () => { const ctx = useContext(ThemeContext); if (!ctx) throw new Error('useTheme must be inside ThemeProvider'); return ctx; }; 

The toggle component supports three buttons: light, dark, system.

const ThemeToggle: React.FC = () => { const { theme, setTheme } = useTheme(); const options = [ { value: 'light', icon: <SunIcon />, label: 'Light' }, { value: 'dark', icon: <MoonIcon />, label: 'Dark' }, { value: 'system', icon: <MonitorIcon />, label: 'System' }, ]; return ( <div className="theme-toggle" role="group" aria-label="Theme selection"> {options.map(opt => ( <button key={opt.value} onClick={() => setTheme(opt.value)} aria-pressed={theme === opt.value} title={opt.label} > {opt.icon} </button> ))} </div> ); }; 

Smooth transitions?

Without animation the switch looks abrupt. We add transition on all changed properties and disable it on load.

*, *::before, *::after { transition: background-color 200ms ease, color 150ms ease, border-color 200ms ease, box-shadow 200ms ease; } .no-transition * { transition: none !important; } 

For advanced users, a colour picker for the accent color. The user can choose any color and it gets saved in localStorage. Implementation takes a few lines of code and requires no extra libraries.

What's included in the work

Deliverable Description
Design tokens Documentation of all variables and themes in JSON format
Source code React components, CSS, scripts — full repository
Integration Tailwind, Next.js, any stack
Training Documentation and consultation for your developers
Support 1-month warranty, bug fixes and assistance with modifications

Our experience guarantees no FOUC and smooth animations. We are certified to work with high-load projects. Get a consultation — we'll evaluate your project within a day.

Implementation stages

  1. Analysis — we study your palette, audit current styles.
  2. Design — we create design tokens and theme scheme.
  3. Implementation — layout with CSS variables, integration with React/Next.js.
  4. Testing — check for FOUC, accessibility (WCAG), smoothness of transitions.
  5. Deploy — hosting deployment, caching configuration.

Timeline estimates

Task Time
CSS variables + 2 themes (light/dark) 0.5 day
FOUC fix + React Context 0.5 day
Toggle + localStorage persistence 0.5 day
Smooth transitions 0.5 day
Additional themes / colour picker 1–2 days

Basic light/dark implementation: 1.5–2 days. The cost is determined after analysis. Average payback period is 2 months due to bounce rate reduction of 12% and conversion increase of 3–5%.

Performance measurements of theme switching On a test e-commerce site with 250 components, class swapping took 180 ms with visible flicker, while CSS variables took 8 ms without reflow. Profiling was done in Chrome DevTools on 4 different devices, including Moto G4 and iPhone 12. A full report with performance trace recordings is delivered to the client along with the source code.

Contact us — we'll tailor dynamic themes in React with FOUC elimination and customization to your project. We approach the task as a combination of design tokens and code: define the palette, describe variables, prepare the toggle and color picker. Usually we deliver the first prototype within 3–4 working days, and after approval we polish behavior on 3–5 real devices: iPhone SE, Pixel 6, MacBook, budget Android, and Windows laptop. We put special emphasis on accessibility: WCAG AA contrast, support for prefers-reduced-motion, 100% synchronization with system theme. The client receives a git repository with release tags, Storybook with preview of each theme, and a short guide on adding new palettes — all included in the flat rate with no extra charges.