Skip to content

Bug: Zustand persisted stores cause hydration mismatch — header flickers on load #368

@gboigwe

Description

@gboigwe

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

  1. During server-side rendering, the store returns default values (address: null, isConnected: false)
  2. During client-side hydration, Zustand immediately reads from localStorage and updates the store
  3. This causes a mismatch between server-rendered HTML and client state
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfrontendNextjs frontend

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions