Ask · Answer · Grow
1. The "State + useEffect" Pattern (Recommended)
The safest way is to start with a null or default state and only update it once the component has mounted on the client.
'use client';
import { useState, useEffect } from 'react';
export default function MyComponent() {
const [data, setData] = useState<string | null>(null);
useEffect(() => {
// This code only runs on the client
const savedData = localStorage.getItem('my-key');
setData(savedData);
}, []);
if (data === null) return <div>Loading...</div>;
return <div>Stored Data: {data}</div>;
}
2. Why this works
Next.js renders the component on the server first. Since useEffect never runs on the server, the server and the initial client render will both see data as null. Once the page "hydrates" (becomes interactive) on your browser, the useEffect fires, updates the state, and triggers a re-render with the actual localStorage value.
3. Alternative: Disable SSR for the Component
If you have a component that relies heavily on localStorage and you want to skip the server-side part entirely, you can import it using next/dynamic with ssr: false.
import dynamic from 'next/dynamic';
const ComponentWithLocalStorage = dynamic(
() => import('../components/MyComponent'),
{ ssr: false }
);
export default function Page() {
return <ComponentWithLocalStorage />;
}
4. Pro-Tip: Use a Custom Hook
If you're doing this often, I'd suggest creating a useLocalStorage hook that handles the "isMounted" logic internally to keep your components clean.Share your knowledge with the community
Sign in to answer
You need to be signed in to contribute an answer.