Clerk Auth in Next.js App Router
Drop-in auth with Clerk, middleware protection, and user metadata
What You’ll Build
After following this guide, you will have a working implementation of clerk auth in next.js app router in your project. Add production-ready authentication to Next.js App Router in under 10 minutes with Clerk. Includes pre-built sign-in/sign-up UI components, middleware-based route protection, and server-side user access. No need to build auth UI from scratch.
Use Cases & Problems Solved
- Protect routes so only authenticated users can access sensitive pages
- Allow users to sign up, log in, and manage their accounts securely
- Avoid storing raw passwords or building session management from scratch
Prerequisites
- Clerk account (free tier available)
- Next.js 14+ with App Router
Step-by-Step Implementation
Install Clerk
The following snippet shows how to install clerk. Copy this into your project and adjust the values for your environment.
npm install @clerk/nextjs
Add environment variables
The following snippet shows how to add environment variables. Copy this into your project and adjust the values for your environment.
# .env.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
Wrap app with ClerkProvider
The following snippet shows how to wrap app with clerkprovider. Copy this into your project and adjust the values for your environment.
// app/layout.js
import { ClerkProvider, SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/nextjs';
export default function RootLayout({ children }) {
return (
<ClerkProvider>
<html lang="en">
<body>
<nav>
<SignedOut><SignInButton /></SignedOut>
<SignedIn><UserButton /></SignedIn>
</nav>
{children}
</body>
</html>
</ClerkProvider>
);
}
Protect routes with middleware
The following snippet shows how to protect routes with middleware. Copy this into your project and adjust the values for your environment.
// middleware.js (project root)
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);
export default clerkMiddleware(async (auth, req) => {
if (isProtectedRoute(req)) await auth.protect();
});
export const config = { matcher: ['/((?!.*\\..*|_next).*)'] };
⚠️ Don’t Do This
❌ Checking auth only on the client side
// Client-only check — data already sent to browser!
export default function SecretPage() {
const { isSignedIn } = useUser();
if (!isSignedIn) redirect('/sign-in');
return <SecretData />; // Too late, HTML was already sent
}
✅ Use middleware for server-side route protection
// middleware.js — blocks request BEFORE page renders
export default clerkMiddleware(async (auth, req) => {
if (isProtectedRoute(req)) await auth.protect();
});
Testing
Add these tests to verify your clerk auth in next.js app router implementation works correctly:
// __tests__/clerk.test.ts
import { describe, it, expect } from 'vitest';
describe('Clerk Auth in Next.js App Router', () => {
it('should handle successful authentication', async () => {
// Test the happy path
const result = await authenticate({ email: 'test@example.com', password: 'valid' });
expect(result.user).toBeDefined();
expect(result.error).toBeNull();
});
it('should reject invalid credentials', async () => {
const result = await authenticate({ email: 'test@example.com', password: 'wrong' });
expect(result.user).toBeNull();
expect(result.error).toBeDefined();
});
it('should handle missing fields', async () => {
const result = await authenticate({ email: '', password: '' });
expect(result.error).toBeDefined();
});
});
Verification
npm run dev
# Visit /dashboard — should redirect to Clerk sign-in
# Sign in — should see UserButton avatar in nav
# Visit /dashboard again — should load successfully Related Specs
Passwordless Magic Link Auth
Email magic link flow with Resend and custom token verification
Next.js Google OAuth Login
Complete Google OAuth flow with NextAuth.js, token handling, session management
Supabase Email/Password Auth
Sign up, sign in, password reset, and protected routes with Supabase Auth