DEV Community

Digital dev
Digital dev

Posted on

Migrating Auth from Vite to Next.js: Supabase, Clerk, and Auth.js Patterns That Actually Work

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);
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

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 />;
}
Enter fullscreen mode Exit fullscreen mode

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;
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

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))
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Switch to Cookies: Stop using LocalStorage for JWTs.
  2. Use Middleware: Handle redirects on the edge, not in useEffect.
  3. Leverage Server Components: Fetch user data on the server to improve LCP and security.

Further reading: ViteToNext.AI - Automated Framework Migration

Top comments (0)