Why can't we just use JWTs in Next.js?


In traditional React + Express apps, it's common to use JWTs (JSON Web Tokens) stored in localStorage for authentication. So when working with Next.js, it's natural to ask:
"Why can't I just use JWTs in Next.js like I do in React?"
The short answer: you can, but you'll break one of Next.js’s core powers — Server-Side Rendering (SSR)
The main reason is – We can't really send the JWT token in the very first request that comes from the browser.
Let’s understand this.
Cookies Are the Norm in Next.js
In a framework like Next.js, most people use cookies for authentication — either through next-auth
, or services like Auth0 or Clerk.
But when you try to manually use JWT stored in localStorage
, things fall apart — especially with SSR (Server Side Rendering).
So What Actually Breaks?
Let’s assume JWT works perfectly. Say you have a /profile
page where you want to show the user’s info from their JWT.
You store the token in localStorage, then on /profile
, fetch and decode the token, and get the user’s info.
Seems fine, right?
But here’s how things work under the hood in Next.js:
The browser sends the very first request to your server — SSR kicks in.
Server tries to render
/profile
.But where is the JWT? It’s in
localStorage
— which the server can’t access.So server sends a blank or loading page.
After hydration, your client-side JS runs, grabs the token from
localStorage
, and then makes another request to actually get the user’s data.
In short: you don’t get the user’s data on the first request — you miss the SSR magic. Actually to get the user’s data you made two requests, both from your browser.
Why Cookies Work
If you store the JWT inside a cookie (preferably an HTTP-only cookie), that cookie gets sent automatically in every request – including that very first SSR request.
Now:
Server sees the cookie immediately
It can decode it
It renders the full personalized page before sending it to the client
That’s how SSR is supposed to work.
Code Examples - with JWTs
/signin/route.tsx
import jwt from "jsonwebtoken";
import { NextRequest, NextResponse } from "next/server";
const SECRET_KEY = "top secret";
export async function POST(req: NextRequest) {
const body = await req.json();
const userId = "123456"
const token = jwt.sign({ userId }, SECRET_KEY);
return NextResponse.json({ token });
}
/profile/route.tsx
import jwt from "jsonwebtoken";
import { NextRequest, NextResponse } from "next/server";
export function GET(req: NextRequest) {
const headers = req.headers;
const authorizationHeader = headers.get("authorization");
const decoded = jwt.decode(authorizationHeader, "SECRET_KEY");
const userId = decoded.userId;
return NextResponse.json({
username: "new user"
});
}
/profile/page.tsx
"use client"
import axios from "axios";
import { useEffect, useState } from "react";
export default function Profile() {
const [username, setUsername] = useState("");
useEffect(() => {
axios.get("http://localhost:3000/api/profile", {
headers: {
authorization: localStorage.getItem("token")
}
}).then(res => {
setProfilePicture(res.data.username);
});
}, []);
return <div>{username}</div>;
}
Why This Fails
localStorage is client-only.
On the very first request, the server doesn’t get the token.
SSR fails to personalize.
Extra requests are needed.
Final Thoughts
If you’re using Next.js and want SSR (which is literally why most people use it), don’t rely on localStorage for auth.
Either:
Store JWTs in HTTP-only cookies
Or use
next-auth
, Auth0, Clerk, etc.
That way, you can access the auth info in the very first request, and render full pages server-side – as intended.
Subscribe to my newsletter
Read articles from Garvit Dadheech directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Garvit Dadheech
Garvit Dadheech
I am a Full Stack, DevOps, and Web3 Developer with expertise in building end-to-end applications. As a fast learner, I quickly adapt to different technology stacks, ensuring the delivery of robust software solutions. I specialize in creating scalable web applications tailored to meet client needs and drive innovation.