Skip to content
Building UI Design Systems

Lesson 5 of 6 · 20 min

x
5/6

Lesson position in the course — not completion. Use Mark Complete to track finished lessons (saved in this browser).

Dark Mode & Dynamic Color Schemes

Implementing robust dark mode requires avoiding layout flash (Flash of Unstyled Content - FOUC) when reading user theme preferences on initial page load. The standard web pattern applies a .dark class to the root <html> element based on user toggles or system prefers-color-scheme media queries.

Libraries like next-themes manage theme persistence in localStorage, inject an inline anti-FOUC script into <head>, and listen for OS theme preference changes automatically.

Before
Manual Theme Switcher with FOUC Flash
1// ❌ Flashes light mode on load before useEffect fires!2useEffect(() => {3  const theme = localStorage.getItem('theme');4  if (theme === 'dark') document.documentElement.classList.add('dark');5}, []);
After
Next-Themes Provider Integration
1// app/providers.tsx2'use client';3import { ThemeProvider } from 'next-themes';4 5export function Providers({ children }: { children: React.ReactNode }) {6  return (7    <ThemeProvider attribute="class" defaultTheme="system" enableSystem>8      {children}9    </ThemeProvider>10  );11}

Exercise

Add a theme toggle button using useTheme() from next-themes that cycles between light, dark, and system modes.

Check your understanding

  • What causes theme flash (FOUC) when implementing dark mode in SSR applications?Show answer

    Answer

    The server renders default light HTML, and the client JavaScript updates the theme class only after downloading and running.
  • How does next-themes eliminate theme flash?Show answer

    Answer

    It injects a tiny inline blocking script into <head> that reads localStorage/system preferences before the page paints.
Previous

Progress is saved in this browser.

Next Lesson