Description
Both the frontend/src/store/wallet-store.ts and frontend/src/store/tx-store.ts use Zustand's persist middleware with localStorage, which causes React hydration mismatches in Next.js:
// wallet-store.ts
storage: createJSONStorage(() =>
typeof window !== "undefined"
? localStorage
: ({
getItem: () => null,
setItem: () => {},
removeItem: () => {},
} as any),
),
Problem
- During server-side rendering, the store returns default values (
address: null, isConnected: false)
- During client-side hydration, Zustand immediately reads from localStorage and updates the store
- This causes a mismatch between server-rendered HTML and client state
- React logs hydration warnings and may re-render the entire tree
The Header component tries to work around this with a mounted state:
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);
if (!mounted) return null;
But this means the header flashes on every page load — it renders nothing during SSR, then appears after hydration.
Impact
- Header flickers/flashes on every page navigation
- React hydration warnings in console
- SEO impact — search engines see no header content
- Poor user experience with layout shift
Suggested Fix
Use Zustand's onRehydrateStorage callback to delay rendering until hydration is complete:
// In the persist config:
onRehydrateStorage: () => (state) => {
state?.setHydrated(true);
},
Or use useSyncExternalStore pattern for SSR-safe store access.
Files Affected
frontend/src/store/wallet-store.ts
frontend/src/store/tx-store.ts
frontend/src/components/header.tsx
Description
Both the
frontend/src/store/wallet-store.tsandfrontend/src/store/tx-store.tsuse Zustand'spersistmiddleware with localStorage, which causes React hydration mismatches in Next.js:Problem
address: null,isConnected: false)The
Headercomponent tries to work around this with amountedstate:But this means the header flashes on every page load — it renders nothing during SSR, then appears after hydration.
Impact
Suggested Fix
Use Zustand's
onRehydrateStoragecallback to delay rendering until hydration is complete:Or use
useSyncExternalStorepattern for SSR-safe store access.Files Affected
frontend/src/store/wallet-store.tsfrontend/src/store/tx-store.tsfrontend/src/components/header.tsx