Implement a flicker-free dark mode in Next.js using Tailwind CSS dark class strategy with system preference detection and user toggle.

Abdur Razzak
Full-Stack Web Developer
There are two main approaches to dark mode in Next.js with Tailwind: the class strategy (add a dark class to the html element and toggle it with JavaScript) and the media strategy (use the prefers-color-scheme media query automatically). The class strategy is recommended because it gives users control to override their system preference — which is what most users expect from a modern website.
In your tailwind.config.js, set darkMode: 'class'. Now all dark: variants in Tailwind (like dark:bg-gray-900 dark:text-white) only apply when an ancestor element has the dark class. Typically you add or remove this class on the html element. Build out your color scheme using both light and dark variants for every color in your design.
The biggest challenge with dark mode in Next.js is preventing the flash of the wrong theme on page load. Because JavaScript runs after HTML renders, there is a brief moment where the page renders in light mode before switching to dark — this is jarring for dark mode users. Solve this by injecting an inline script in the <head> that reads localStorage before React hydrates and sets the dark class synchronously.
The next-themes library handles all the complexity for you: it prevents flash, syncs with system preference, persists user choice to localStorage, and provides a ThemeProvider and useTheme hook. Install next-themes, wrap your app in ThemeProvider with attribute='class', and use the setTheme function from useTheme in your toggle button. This is the recommended approach for most Next.js projects.
Create a ThemeToggle component that uses the useTheme hook from next-themes. Show a sun icon for light mode and a moon icon for dark mode. Use suppressHydrationWarning on the button to prevent React hydration mismatch warnings, since the theme cannot be known until after client-side JavaScript runs. Animate the icon transition with a smooth CSS transition for a polished feel.
Use CSS custom properties (variables) for your color palette and define dark mode overrides in a [data-theme=dark] selector or .dark selector. Avoid pure black (#000000) backgrounds in dark mode — use dark grays like #0f0f0f or #1a1a1a for better readability. Reduce image brightness slightly in dark mode with dark:brightness-90 to prevent images from being too harsh against dark backgrounds.