Clerk Authentication

Battle-tested patterns for Clerk authentication in Next.js applications.

Overview#

Clerk provides a complete authentication solution with:

  • Pre-built UI components
  • Social login providers
  • Multi-factor authentication
  • User management dashboard
  • Webhook support

Prerequisites:

  • Clerk account and API keys
  • Next.js 14+ with App Router

Installation#

npm install @clerk/nextjs
1# .env.local 2NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... 3CLERK_SECRET_KEY=sk_test_... 4NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in 5NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up 6NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard 7NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard

Implementation#

Server-Side Auth Check#

1// lib/auth.ts 2import { auth } from '@clerk/nextjs/server' 3 4export async function requireAuth() { 5 const { userId } = await auth() 6 7 if (!userId) { 8 throw new Error('UNAUTHORIZED') 9 } 10 11 return userId 12} 13 14// Usage in Server Action 15export async function myAction() { 16 const userId = await requireAuth() 17 // Continue with authenticated user 18}

Protected Layout#

1// app/(protected)/layout.tsx 2import { auth } from '@clerk/nextjs/server' 3import { redirect } from 'next/navigation' 4 5export default async function ProtectedLayout({ 6 children 7}: { 8 children: React.ReactNode 9}) { 10 const { userId } = await auth() 11 12 if (!userId) { 13 redirect('/sign-in') 14 } 15 16 return <>{children}</> 17}

Get Current User with Metadata#

1// lib/auth.ts 2import { currentUser } from '@clerk/nextjs/server' 3import { prisma } from '@/lib/db' 4 5export async function getCurrentUser() { 6 const user = await currentUser() 7 if (!user) return null 8 9 // Get or create database user 10 const dbUser = await prisma.user.upsert({ 11 where: { clerkId: user.id }, 12 update: { 13 email: user.emailAddresses[0]?.emailAddress, 14 name: `${user.firstName} ${user.lastName}`.trim() 15 }, 16 create: { 17 clerkId: user.id, 18 email: user.emailAddresses[0]?.emailAddress ?? '', 19 name: `${user.firstName} ${user.lastName}`.trim() 20 } 21 }) 22 23 return dbUser 24}

Client-Side Auth Hook#

1// hooks/use-user.ts 2'use client' 3import { useUser } from '@clerk/nextjs' 4 5export function useCurrentUser() { 6 const { user, isLoaded, isSignedIn } = useUser() 7 8 return { 9 user: isSignedIn ? user : null, 10 isLoading: !isLoaded, 11 isAuthenticated: isSignedIn 12 } 13}

Middleware Configuration#

1// middleware.ts 2import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' 3 4const isProtectedRoute = createRouteMatcher([ 5 '/dashboard(.*)', 6 '/settings(.*)', 7 '/api/user(.*)' 8]) 9 10const isPublicRoute = createRouteMatcher([ 11 '/', 12 '/sign-in(.*)', 13 '/sign-up(.*)', 14 '/api/webhooks(.*)' 15]) 16 17export default clerkMiddleware(async (auth, req) => { 18 if (isProtectedRoute(req)) { 19 await auth.protect() 20 } 21}) 22 23export const config = { 24 matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'] 25}

Usage Examples#

Server Component#

1// app/dashboard/page.tsx 2import { auth } from '@clerk/nextjs/server' 3import { redirect } from 'next/navigation' 4 5export default async function DashboardPage() { 6 const { userId } = await auth() 7 8 if (!userId) { 9 redirect('/sign-in') 10 } 11 12 return <Dashboard userId={userId} /> 13}

Server Action#

1// actions/profile.ts 2'use server' 3 4import { auth } from '@clerk/nextjs/server' 5import { prisma } from '@/lib/db' 6 7export async function updateProfile(formData: FormData) { 8 const { userId } = await auth() 9 10 if (!userId) { 11 throw new Error('Unauthorized') 12 } 13 14 await prisma.user.update({ 15 where: { clerkId: userId }, 16 data: { 17 name: formData.get('name') as string 18 } 19 }) 20}

Best Practices#

  1. Always sync with database - Create a database user record to store app-specific data
  2. Use middleware for route protection - Centralize auth checks in middleware
  3. Handle loading states - Show loading UI while Clerk initializes on client
  4. Set up webhooks - Listen for user.created, user.updated, user.deleted events
  5. Use Server Components - Prefer server-side auth checks when possible