Introduction
Moving a frontend application from a Client-Side Rendered (CSR) Vite environment to a Server-Side Rendered (SSR) Next.js environment is a major architectural shift. While the UI components might remain the same, the way you handle authentication changes fundamentally. In Vite, your auth logic usually lives entirely in the browser (LocalStorage/Cookies). In Next.js, auth needs to be synchronized between the client and the server to prevent the dreaded "flash of unauthenticated content."
In this guide, we will explore how to migrate the three most popular authentication patterns from Vite to Next.js App Router: Supabase, Clerk, and Auth.js.
The Core Shift: Client vs. Server Context
In a standard Vite + React SPA, you likely use a useAuth hook that checks for a JWT in localStorage. In Next.js, this isn't enough. Because Next.js renders on the server first, the server needs to know who the user is before the page is even sent to the browser.
This means migrating from Local Storage to HttpOnly Cookies.
1. Migrating Supabase Auth
Supabase makes the transition relatively smooth, but you must swap your supabase-js client for the @supabase/ssr package.
The Vite Way
In Vite, you usually initialize the client once:
export const supabase = createClient(URL, KEY);
The Next.js Way
You need to create a client that can access cookies on the server. You'll typically create two versions: one for Server Components and one for Client Components.
Server Action Example:
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function getSession() {
const cookieStore = cookies()
const supabase = createServerClient(process.env.URL!, process.env.KEY!, {
cookies: {
get(name) { return cookieStore.get(name)?.value },
set(name, value, options) { cookieStore.set({ name, value, ...options }) },
remove(name, options) { cookieStore.set({ name, '', ...options }) },
},
})
return await supabase.auth.getUser()
}
2. Migrating Clerk Auth
Clerk is arguably the easiest to migrate because it was built with a "Next.js-first" mindset.
The Vite Way
You wrapped your app in <ClerkProvider> and used useAuth() hooks throughout your components.
The Next.js Way
You still use the provider, but you now have access to the auth() helper for Server Components. This allows you to protect routes at the layout level without waiting for the client-side JS to hydrate.
import { auth } from '@clerk/nextjs/server';
export default async function Page() {
const { userId } = auth();
if (!userId) return <div>Please sign in</div>;
return <Dashboard />;
}
If you find the architectural shift of moving these auth patterns manually to be time-consuming, you can use ViteToNext.AI to automate the heavy lifting of refactoring your React structure into a Next.js compatible format.
3. Migrating to Auth.js (formerly NextAuth)
If you were using a custom backend or Firebase with Vite, many developers choose this moment to switch to Auth.js for its deep integration with the Next.js ecosystem.
The Pattern
Auth.js handles the session cookie management for you. The biggest hurdle here is moving your logic from useEffect hooks into the auth.ts configuration file.
Defining the Config:
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [GitHub],
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user;
// Logic to protect specific routes
return isLoggedIn;
},
},
})
Handling Route Guards
In Vite, you likely used a <ProtectedRoute> component wrapping your <Route>. In Next.js, we use Middleware.
Middleware runs before any request is completed, allowing you to redirect unauthorized users before the page even begins to render. This eliminates the flickering associated with client-side redirects.
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/request'
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
Conclusion
Migrating auth from Vite to Next.js is less about changing your auth provider and more about changing your data flow. You are moving from a world where "The Browser knows all" to a world where "The Server validates first."
Key takeaways:
- Switch to Cookies: Stop using LocalStorage for JWTs.
- Use Middleware: Handle redirects on the edge, not in
useEffect. - Leverage Server Components: Fetch user data on the server to improve LCP and security.
Further reading: ViteToNext.AI - Automated Framework Migration
Top comments (0)