Clerk Authentication
Status: Production Ready ✅ Last Updated: 2025-10-28 Dependencies: None Latest Versions: @clerk/nextjs@6.33.3, @clerk/backend@2.17.2, @clerk/clerk-react@5.51.0, @clerk/testing@1.4.4
Quick Start (10 Minutes)
Choose your framework:
- [React (Vite)](#react-vite-setup) - ClerkProvider + hooks
- [Next.js App Router](#nextjs-app-router-setup) - Middleware + async auth()
- [Cloudflare Workers](#cloudflare-workers-setup) - Backend verification
React (Vite) Setup
1. Install Clerk
\\\bash npm install @clerk/clerk-react \\\
Latest Version: @clerk/clerk-react@5.51.0 (verified 2025-10-22)
2. Configure ClerkProvider
Update \src/main.tsx\:
\\\`typescript import React from 'react' import ReactDOM from 'react-dom/client' import { ClerkProvider } from '@clerk/clerk-react' import App from './App.tsx' import './index.css'
// Get publishable key from environment const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) { throw new Error('Missing Publishable Key') }
ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <ClerkProvider publishableKey={PUBLISHABLE_KEY}> <App /> </ClerkProvider> </React.StrictMode>, ) \\\`
CRITICAL:
- Use \
VITE_\prefix for environment variables in Vite - ClerkProvider must wrap your entire app
- Source: https://clerk.com/docs/references/react/clerk-provider
3. Add Environment Variables
Create \.env.local\:
\\\bash VITE_CLERK_PUBLISHABLE_KEY=pk_test_... \\\
Security Note: Only \VITE_\ prefixed vars are exposed to client code.
4. Use Authentication Hooks
\\\`typescript import { useUser, useAuth, useClerk } from '@clerk/clerk-react'
function App() { // Get user object (includes email, metadata, etc.) const { isLoaded, isSignedIn, user } = useUser()
// Get auth state and session methods const { userId, sessionId, getToken } = useAuth()
// Get Clerk instance for advanced operations const { openSignIn, signOut } = useClerk()
// Always check isLoaded before rendering auth-dependent UI if (!isLoaded) { return <div>Loading...</div> }
if (!isSignedIn) { return <button onClick={() => openSignIn()}>Sign In</button> }
return ( <div> <h1>Welcome {user.firstName}!</h1> <p>Email: {user.primaryEmailAddress?.emailAddress}</p> <button onClick={() => signOut()}>Sign Out</button> </div> ) } \\\`
Why This Matters:
- \
isLoaded\prevents flash of wrong content - \
useUser()\provides full user object with metadata - \
useAuth()\provides session tokens and auth state - Source: https://clerk.com/docs/references/react/use-user
Next.js App Router Setup
1. Install Clerk
\\\bash npm install @clerk/nextjs \\\
Latest Version: @clerk/nextjs@6.33.3 (verified 2025-10-22)
- New in v6: Async auth() helper, Next.js 15 support, static rendering by default
- Source: https://clerk.com/changelog/2024-10-22-clerk-nextjs-v6
2. Configure Environment Variables
Create \.env.local\:
\\\`bash NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_...
Optional: Customize sign-in/up pages
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding \\\`
CRITICAL:
- \
CLERK_SECRET_KEY\must NEVER be exposed to client - Use \
NEXT_PUBLIC_\prefix for client-side vars only - Source: https://clerk.com/docs/guides/development/clerk-environment-variables
3. Add Middleware for Route Protection
Create \middleware.ts\ in project root:
\\\`typescript import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
// Define which routes are public (everything else requires auth) const isPublicRoute = createRouteMatcher([ '/', '/sign-in(.)', '/sign-up(.)', '/api/webhooks(.*)', // Clerk webhooks should be public ])
export default clerkMiddleware(async (auth, request) => { // Protect all routes except public ones if (!isPublicRoute(request)) { await auth.protect() } })
export const config = { matcher: [ // Skip Next.js internals and static files '/((?!_next|[^?]\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).)', // Always run for API routes '/(api|trpc)(.*)', ], } \\\`
CRITICAL:
- \
auth.protect()\is async in v6 (breaking change from v5) - \
createRouteMatcher()\accepts glob patterns - Alternative: protect specific routes instead of inverting logic
- Source: https://clerk.com/docs/reference/nextjs/clerk-middleware
4. Wrap App with ClerkProvider
Update \app/layout.tsx\:
\\\`typescript import { ClerkProvider } from '@clerk/nextjs' import './globals.css'
export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <ClerkProvider> <html lang="en"> <body>{children}</body> </html> </ClerkProvider> ) } \\\`
5. Use auth() in Server Components
\\\`typescript import { auth, currentUser } from '@clerk/nextjs/server'
export default async function DashboardPage() { // Get auth state (lightweight) const { userId, sessionId } = await auth()
// Get full user object (heavier, fewer calls) const user = await currentUser()
if (!userId) { return <div>Unauthorized</div> }
return ( <div> <h1>Dashboard</h1> <p>User ID: {userId}</p> <p>Email: {user?.primaryEmailAddress?.emailAddress}</p> </div> ) } \\\`
CRITICAL:
- \
auth()\is async in v6 (breaking change) - Use \
auth()\for lightweight checks - Use \
currentUser()\when you need full user object
Cloudflare Workers Setup
1. Install Dependencies
\\\bash npm install @clerk/backend hono \\\
Latest Versions:
- @clerk/backend@2.17.2 (verified 2025-10-22)
- hono@4.10.1
2. Configure Environment Variables
Create \.dev.vars\ for local development:
\\\bash CLERK_SECRET_KEY=sk_test_... CLERK_PUBLISHABLE_KEY=pk_test_... \\\
Production: Use \wrangler secret put CLERK_SECRET_KEY\
3. Implement Token Verification
Create \src/index.ts\:
\\\`typescript import { Hono } from 'hono' import { verifyToken } from '@clerk/backend'
type Bindings = { CLERK_SECRET_KEY: string CLERK_PUBLISHABLE_KEY: string }
type Variables = { userId: string | null sessionClaims: any | null }
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()
// Middleware: Verify Clerk token app.use('/api/*', async (c, next) => { const authHeader = c.req.header('Authorization')
if (!authHeader) { c.set('userId', null) c.set('sessionClaims', null) return next() }
const token = authHeader.replace('Bearer ', '')
try { const { data, error } = await verifyToken(token, { secretKey: c.env.CLERK_SECRET_KEY, // IMPORTANT: Set authorizedParties to prevent CSRF attacks authorizedParties: ['https://yourdomain.com'], })
if (error) { console.error('Token verification failed:', error) c.set('userId', null) c.set('sessionClaims', null) } else { c.set('userId', data.sub) c.set('sessionClaims', data) } } catch (err) { console.error('Token verification error:', err) c.set('userId', null) c.set('sessionClaims', null) }
return next() })
// Protected route app.get('/api/protected', (c) => { const userId = c.get('userId')
if (!userId) { return c.json({ error: 'Unauthorized' }, 401) }
return c.json({ message: 'This is protected', userId, sessionClaims: c.get('sessionClaims'), }) })
export default app \\\`
CRITICAL:
- Always set \
authorizedParties\to prevent CSRF attacks - Use \
secretKey\, not deprecated \apiKey\ - Source: https://clerk.com/docs/reference/backend/verify-token