How do you stop SSR hydration from flashing the wrong user state at the edge?

What’s up everyone? I’m wiring up SSR + edge rendering for a Next.js-ish app, and I’m trying to avoid a nasty flicker where the server renders “logged out” but the client immediately hydrates into “logged in”, so the header swaps and analytics double-fire.

export async function getServerProps(req) {
  const token = req.cookies.session;
  const user = token ? await fetchUser(token) : null;
  return { user };
}

function Header({ user }) {
  const [clientUser, setClientUser] = useState(user);

  useEffect(() => {
    fetch("/api/me", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(setClientUser);
  }, []);

  return <div>{clientUser ? "Welcome" : "Sign in"}</div>;
}

In real life the edge cache sometimes serves a stale logged-out HTML shell, and then hydration “corrects” it; what’s the cleanest pattern to prevent the UI swap and duplicate side effects without killing caching?

1 Like

I’d stop the edge HTML from pretending it knows the user.

Render a neutral header on the server — “Account”, skeleton, whatever — then resolve auth on the client from /api/me or a non-cached personalized request. That avoids the logged-out → logged-in swap, and it keeps the cache useful.

For analytics, fire only after auth is resolved. One authResolved flag is enough; otherwise the page view runs once for the shell and again when the user state lands.