Skip to main content
The Starter Kit includes a complete dark mode implementation using next-themes and Tailwind CSS. Users can toggle between light, dark, and system themes with their preference persisted across sessions.

Overview

The theme system provides:
  • Light and dark mode with instant switching
  • System preference detection that respects OS settings
  • Persistent user selection stored in localStorage
  • No flash of unstyled content on page load
  • Tailwind integration using dark: class modifiers

Architecture

ThemeProvider Setup

The theme system is initialized in the root layout:
app/layout.tsx
attribute
string
default:"class"
How the theme is applied. Using "class" enables Tailwind’s dark: modifiers.
defaultTheme
string
default:"system"
The default theme when no preference is stored. "system" follows the OS preference.
enableSystem
boolean
default:true
Allows detecting and using the system theme preference.
disableTransitionOnChange
boolean
default:false
Prevents CSS transitions during theme switches for instant changes. Set to true to avoid visual glitches.
The suppressHydrationWarning prop on the <html> element prevents hydration warnings caused by next-themes modifying the class attribute before React hydrates.

ThemeSwitcher Component

The header includes a theme toggle button:
components/theme-switcher.tsx
The component returns null until mounted to prevent hydration mismatches, since the theme is determined client-side.

Tailwind Configuration

Enabling Dark Mode

Dark mode is enabled in tailwind.config.ts:
tailwind.config.ts
darkMode
string
default:"media"
Set to "class" to use next-themes with Tailwind’s dark: modifier. The alternative "media" uses CSS media queries only.

Dark Mode Color Palette

Define colors that work in both themes:
tailwind.config.ts

Styling Components for Dark Mode

Basic Dark Mode Styles

Use Tailwind’s dark: modifier to apply dark mode styles:

Component Example

Create theme-aware components:
components/ui/card.tsx

Complex Color Transitions

Handle colors that need different values in each theme:

CSS Variables Approach

For dynamic theming, use CSS variables:

Define Variables

app/globals.css

Use in Tailwind

Reference CSS variables in Tailwind config:
tailwind.config.ts

Apply in Components

Using HSL color values in CSS variables makes it easy to adjust saturation and lightness programmatically.

Advanced Theme Patterns

Multi-Theme Dropdown

Create a full theme selector with light, dark, and system options:
components/theme-selector.tsx

Detecting System Theme

Access the resolved theme (accounting for system preference):

Conditional Rendering

Render different content based on theme:

Custom Color Schemes

Creating Theme Variants

Define multiple color schemes:
app/globals.css
Implement color scheme switching:

Best Practices

Use semantic color names like background, foreground, border instead of specific colors like white or gray-900. This makes theme changes easier.
Always test your UI in both light and dark modes. Colors that work in light mode may have insufficient contrast in dark mode.
Ensure WCAG AA contrast ratios (4.5:1 for text) in both themes. Use tools like Contrast Checker.
Set disableTransitionOnChange to prevent CSS transitions from animating during theme switches, which can cause visual glitches.
For logos and images, provide theme-specific variants or use images that work in both themes. Transparent PNGs and SVGs work best.
next-themes automatically persists the user’s theme preference in localStorage. No additional code needed.

Accessibility Considerations

Respecting System Preferences

The default system theme respects the user’s OS-level preference:
This ensures users who have set a dark mode preference at the OS level see dark mode by default.

Reduced Motion

Respect the prefers-reduced-motion setting:

Focus Indicators

Ensure focus indicators are visible in both themes:

Troubleshooting

If you see a flash when the page loads, ensure suppressHydrationWarning is on the <html> element:
Ensure next-themes can access localStorage. Check browser settings and privacy modes. The theme is stored in localStorage.theme.
If theme toggle icons don’t switch properly, verify both icons are present and CSS transitions are working:
If you see hydration errors, ensure theme-dependent components use the mounted check:

Next Steps

Branding & Styling

Apply your brand colors and visual identity

App Configuration

Configure navigation and layout settings

Component Library

Explore theme-aware UI components

Custom Pages

Create custom pages with dark mode support