When I deployed my SaaS (Next.js app) to production, I noticed something strange. On the first load, my console showed session undefined, but when I refreshed the page (for example /dashboard), everything worked perfectly. Even more confusing, this never happened on localhost—it only happened in production. At first, I thought my auth token was broken, but it turned out it wasn’t. This blog explains what was actually happening, why this bug appears only in production, and how to fix it properly.
The setup I was using was very common: Next.js (App Router), NextAuth, useSession() on the client, and a protected dashboard page.
he problem appeared on the initial page load. I had the following code:
const { data: session } = useSession();
console.log(session);
The output was undefined. However, after refreshing the page, the output became { user: {...}, expires: "..." }. This raised the main question: why is the session undefined on first load but works after refresh?
The real reason is that useSession() is asynchronous. This was the key thing I misunderstood at first. When the page renders, React renders components immediately, and at the same time useSession() starts fetching the session. At that moment, the session is not available yet, so console.log(session) prints session = undefined with status = "loading". This is expected behavior.
This issue does not usually appear on localhost because of speed differences. On localhost, the app is slower, so the session often resolves early before you notice the problem. In production, the app loads much faster, so the render happens before the session is ready, which exposes the issue.
The correct fix on the client side starts with always checking the session status:
const { data: session, status } = useSession();
if (status === "loading") {
return null;
}
if (!session) {
redirect("/login");
}
The second important fix is guarding your useEffect so it does not run before the session exists:
useEffect(() => {
if (!session) return;
fetchBills(session.user.id);
}, [session]);
The big lesson I learned from this is that rendering happens before async data is ready, so you should always assume undefined at least once. This does not apply only to useSession(), but also to API calls, database fetches, React Query, and any asynchronous data in React.
Production bugs don’t mean you’re bad—they mean you’re learning real-world development.
Thanks for reading. If this helped you, feel free to share or connect with me.
Athashri Keny
Top comments (0)