Two strategies for dark mode

Tailwind has a dark: prefix that applies styles only in dark mode. The question is: what triggers dark mode? There are two options, controlled by the darkMode setting in tailwind.config.js.

Media query strategy (default):

// tailwind.config.js
module.exports = {
  darkMode: 'media', // or omit entirely, this is the default
};

Styles prefixed with dark: activate when the user’s OS is set to dark mode (prefers-color-scheme: dark).

Class strategy:

module.exports = {
  darkMode: 'class',
};

Styles prefixed with dark: activate when a parent element (usually <html>) has the dark class.

Using the dark: prefix

With either strategy, the syntax in your markup is identical:

<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
  <h1 class="text-2xl font-bold">Hello</h1>
  <p class="text-gray-600 dark:text-gray-400">Some content here.</p>
</div>

In light mode: white background, dark text. In dark mode: near-black background, light text. The dark: variants are generated for every utility, so you can dark-mode any property.

The media query strategy

With darkMode: 'media', dark mode is entirely driven by the operating system. You write your styles, and the user’s preference controls the toggle. No JavaScript required.

The limitation: you cannot build a manual toggle. If a user wants to override their OS setting and use light mode on a dark-mode OS, you cannot do that with the media strategy. The OS is the only control.

For sites where matching the OS preference is always correct — documentation, marketing pages — the media strategy is simpler and needs no JavaScript.

The class strategy

With darkMode: 'class', Tailwind applies dark styles when <html> has the dark class (or whichever element you configure as the selector). You manage that class with JavaScript.

A simple implementation that respects OS preference by default but allows toggling:

// On page load, check for saved preference
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
  document.documentElement.classList.add('dark');
}

// Toggle function
function toggleDarkMode() {
  const isDark = document.documentElement.classList.toggle('dark');
  localStorage.setItem('theme', isDark ? 'dark' : 'light');
}

In React, this often lives in a context provider:

function ThemeProvider({ children }) {
  const [dark, setDark] = useState(() => {
    const saved = localStorage.getItem('theme');
    if (saved) return saved === 'dark';
    return window.matchMedia('(prefers-color-scheme: dark)').matches;
  });

  useEffect(() => {
    document.documentElement.classList.toggle('dark', dark);
    localStorage.setItem('theme', dark ? 'dark' : 'light');
  }, [dark]);

  return (
    <ThemeContext.Provider value={{ dark, toggle: () => setDark(d => !d) }}>
      {children}
    </ThemeContext.Provider>
  );
}

Avoiding flash of wrong theme

With the class strategy, there is a risk: if you set the dark class in a React effect, the page renders in light mode first, then flips to dark. This is a visible flash.

The fix is to run the theme detection in a <script> tag before React hydrates, in the <head>:

<script>
  (function() {
    const saved = localStorage.getItem('theme');
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    if (saved === 'dark' || (!saved && prefersDark)) {
      document.documentElement.classList.add('dark');
    }
  })();
</script>

This runs synchronously before any rendering, so the correct theme is applied before the user sees anything.

Which strategy to choose

Use media if:

  • You don’t need a manual toggle
  • The site should always match the OS preference
  • You want zero JavaScript for theme handling

Use class if:

  • You are building a user-controlled theme toggle
  • The app has an authenticated user whose preference you store on the server
  • You need the same UI to support both themes simultaneously (e.g., a component preview tool)

Most applications with any kind of user preference management should use the class strategy.