Back to Bugs
closedLowP3

Dark mode toggle doesn't persist after navigation

Opened Jun 3, 2026, 10:43 PMUpdated Jun 5, 2026, 07:26 PM

Description

Toggling dark mode works on the current page, but navigating to another page via client-side routing resets the theme to light mode. Full page refresh preserves the preference correctly (reads from localStorage). Only SPA navigation loses the state.

Reproduction Steps

  1. 1

    Open any page in light mode

  2. 2

    Toggle dark mode via settings icon

  3. 3

    Observe: page correctly switches to dark mode

  4. 4

    Click a navigation link (e.g., go to /products)

  5. 5

    Observe: page renders in light mode again

  6. 6

    Check localStorage: theme preference still says 'dark'

  7. 7

    Refresh the page — dark mode is restored

Root Cause Hypothesis

The ThemeProvider reads from localStorage on mount, but the component doesn't remount during client-side navigation (Next.js keeps the layout mounted). The theme state is initialized correctly on first load but the CSS class on <html> gets overwritten by a competing script in _document that runs on route change.

Resolution

Removed the inline script from _document that was setting the theme class. Moved all theme initialization logic into the ThemeProvider useEffect, which now syncs both the React state and the DOM class attribute. Added a MutationObserver as a safety net to detect external class changes on <html>.