New Oct 1, 2026

Next.js App Router: "Hydration failed because the initial UI does not match what was rendered on the server" when reading localStorage in useState [duplicate]

Libraries, Frameworks, etc. All from Newest questions tagged reactjs - Stack Overflow View Next.js App Router: "Hydration failed because the initial UI does not match what was rendered on the server" when reading localStorage in useState [duplicate] on stackoverflow.com

Next.js App Router: "Hydration failed because the initial UI does not match what was rendered on the server" when reading localStorage in useState

Body:

I'm using Next.js 14 (App Router). I want to store the user's theme preference in localStorage and read it on the first render, but I get a hydration error.

jsx

"use client";
import { useState } from "react";

export default function ThemeToggle() { const [theme, setTheme] = useState( localStorage.getItem("theme") || "light" );

return ( <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}> Current theme: {theme} </button> ); }

Expected: The button shows the saved theme.

Actual: On a hard refresh I get this error:

ReferenceError: localStorage is not defined

and, after wrapping it in typeof window !== "undefined", this one instead:

Error: Hydration failed because the initial UI does not match what was rendered on the server.

What I tried:

  1. Checking typeof window !== "undefined" inside useState, which fixed the first error but caused the hydration mismatch.

  2. Moving the code into useEffect, but then the UI flashes the wrong theme for a moment.

What is the correct way to read localStorage on the first render without a hydration error or a flicker?

Scroll to top