DEV Community

Digital dev
Digital dev

Posted on

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

The Architectural Shift: Client-Side vs. Full-Stack Auth

When you build a Vite application, authentication typically lives entirely in the browser. You likely manage a user state via a useAuth hook, store a JWT in localStorage, and send it in a header to an external API.

Transitioning to Next.js changes the game. You move from a purely client-side model to a hybrid model where authentication must be verified on both the client (for UI) and the server (for Middleware and Server Components). In this guide, we’ll look at how to port the three most popular auth providers from Vite to Next.js without losing your mind.

1. Supabase: Moving from Client-SDK to SSR

In a standard Vite app, you usually initialize the Supabase client once and export it. In Next.js, you need a way to handle cookies automatically so that the server knows who the user is during the initial page load.

The Vite Way (Client Only)

// vite-project/supabase.ts
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(URL, KEY);
Enter fullscreen mode Exit fullscreen mode

The Next.js Way (SSR-Friendly)

Next.js requires using @supabase/ssr. This allows the auth state to be synchronized between the browser and the server via cookies.

// app/utils/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export function createClient() {
  const cookieStore = cookies()
  return createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
    cookies: {
      getAll() { return cookieStore.getAll() },
      setAll(cookiesToSet) {
        cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options))
      },
    },
  })
}
Enter fullscreen mode Exit fullscreen mode

Tip: Don't forget to implement a middleware.ts file to refresh the user's session before they hit your routes. This replaces the manual useEffect checks you likely used in Vite.

2. Clerk: The Easiest Migration Path

Clerk is arguably the simplest to migrate because their SDK is built with Next.js in mind. In Vite, you likely wrapped your app in <ClerkProvider>. In Next.js, the logic remains similar, but you gain access to auth() for Server Components.

The Migration Pattern

  1. Wrap the Root Layout: Move your provider to app/layout.tsx.
  2. Replace useAuth: In your Server Components, instead of client-side hooks, use the asynchronous auth() helper to protect routes before they even render.
// app/dashboard/page.tsx
import { auth } from '@clerk/nextjs/server';

export default async function Page() {
  const { userId } = await auth();
  if (!userId) return <div>Not logged in</div>;

  return <main>Welcome, {userId}</main>;
}
Enter fullscreen mode Exit fullscreen mode

3. Auth.js (Formerly NextAuth): The Native Choice

If you were using a custom OAuth flow or Firebase in Vite, moving to Auth.js is often the right move for Next.js. Auth.js is designed specifically for the Web Crypto API and Edge environments.

Unlike Vite-based solutions where you handle the redirect logic manually, Auth.js handles the /api/auth/* routes for you. You define your providers (GitHub, Google, Email) in a single config file and use the signIn() and signOut() methods provided by the library.

Automating the Transition

Migrating the entire folder structure, updating environment variables, and refactoring API routes from a SPA to a file-system based router can be tedious. If you want to speed up the process, tools like ViteToNext.AI can automatically refactor your Vite + React project into a Next.js structure, allowing you to focus on the auth logic rather than boilerplate.

Strategy: Handling Protected Routes

In Vite, you probably used a <ProtectedRoute> component that wrapped your <Route> in React Router.

In Next.js, move this logic to middleware.ts. This is significantly more secure as it prevents the browser from even downloading the page data if the user isn't authenticated.

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const session = request.cookies.get('session')
  if (!session && 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 provider and more about changing your mindset from "Client-side storage" to "Cookie-based SSR". Whether you choose Supabase for its backend features, Clerk for its DX, or Auth.js for its flexibility, ensure you leverage Middleware and Server Components to make your app truly secure.

Further reading: ViteToNext.AI Migration Guide

Top comments (0)