Clerk Auth Setup in 10 Minutes - 2026 Guide
Step-by-step Clerk authentication setup for Next.js. Real errors, production code patterns, and exactly what you need to ship.
TL;DR
Clerk handles auth so you don't have to. Install SDK, grab API keys, wrap your app in <ClerkProvider>, protect routes with withAuth(), and ship. 10 minutes tops. Verify current pricing and version numbers in [official Clerk docs](https://clerk.com/docs).
---
Why Clerk?
If you're building an indie product, you've got two paths: roll your own auth (months of security headaches) or use a service that handles OAuth, passkeys, MFA, and social login out-of-the-box. Clerk does this without requiring a PhD in cryptography.
The appeal: set up in minutes, scale to millions of users, zero ops overhead.
---
Prerequisites
node --version)For a quick start, create a new Next.js project:
```bash npx create-next-app@latest my-app --typescript cd my-app ```
---
Step 1: Create a Clerk Account & Get Keys
1. Head to [clerk.com](https://clerk.com)
2. Sign up and create a new application
3. Copy your Publishable Key and Secret Key
4. Create .env.local in your project root:
```env NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx CLERK_SECRET_KEY=sk_test_xxxxx NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/ NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/ ```
Note: Verify current environment variable names in [Clerk's environment setup docs](https://clerk.com/docs/deployments/clerk-environment-variables) as these may shift with SDK versions.
---
Step 2: Install Clerk SDK
```bash npm install @clerk/nextjs ```
Verify the latest version in your package.json. [Check official installation docs](https://clerk.com/docs/quickstarts/nextjs) for version-specific setup.
---
Step 3: Wrap Your App with ClerkProvider
For App Router (recommended), modify app/layout.tsx:
```typescript import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <ClerkProvider> <html lang="en"> <body>{children}</body> </html> </ClerkProvider> ); } ```
For Pages Router, wrap your _app.tsx:
```typescript import type { AppProps } from 'next/app'; import { ClerkProvider } from '@clerk/nextjs';
export default function App({ Component, pageProps }: AppProps) { return ( <ClerkProvider> <Component {...pageProps} /> </ClerkProvider> ); } ```
---
Step 4: Create Auth Routes
Clerk provides pre-built sign-in and sign-up components. Create these pages:
app/sign-in/[[...index]].tsx
```typescript import { SignIn } from '@clerk/nextjs';
export default function SignInPage() { return ( <div className="flex items-center justify-center min-h-screen"> <SignIn /> </div> ); } ```
app/sign-up/[[...index]].tsx
```typescript import { SignUp } from '@clerk/nextjs';
export default function SignUpPage() { return ( <div className="flex items-center justify-center min-h-screen"> <SignUp /> </div> ); } ```
The [[...index]] dynamic route handles Clerk's internal routing.
---
Step 5: Protect Routes
Server-Side Protection (Recommended)
For API routes, use auth() middleware in app/api/protected/route.ts:
```typescript import { auth } from '@clerk/nextjs'; import { NextResponse } from 'next/server';
export async function GET() { const { userId } = await auth();
if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); }
return NextResponse.json({
message: Hello, user ${userId},
});
}
```
Client-Side Components
Use useUser() hook to access user data:
```typescript 'use client';
import { useUser } from '@clerk/nextjs';
export default function Dashboard() { const { user, isLoaded } = useUser();
if (!isLoaded) return <div>Loading...</div>;
return ( <div> <h1>Welcome, {user?.firstName}!</h1> <p>Email: {user?.primaryEmailAddress?.emailAddress}</p> </div> ); } ```
Middleware for Page-Level Protection
Create middleware.ts in your project root:
```typescript import { authMiddleware } from '@clerk/nextjs';
export default authMiddleware({ publicRoutes: ['/', '/about', '/sign-in', '/sign-up'], });
export const config = { matcher: [ '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|cur|heic|heif)(?:$|\\?)|_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)', ], }; ```
---
Real Error Messages You'll Hit
Error 1: Missing Environment Variables
``` Error: Missing Publishable Key. Make sure to set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY ```
Fix: Verify .env.local exists and restart your dev server (npm run dev).
Error 2: Provider Not Wrapping App
``` Error: useUser() must be used within <ClerkProvider> ```
Fix: Ensure <ClerkProvider> wraps your entire app in layout.tsx/_app.tsx.
Error 3: Middleware Matcher Issues
``` Warning: Routing instrumentation skipped, this logger only works in the edge runtime ```
Fix: Update your middleware matcher regex. See [Clerk middleware docs](https://clerk.com/docs/nextjs/middleware).
---
Production Checklist
---
Related Reading
---
What am I missing?
This guide covers the happy path. What did I skip?
Please comment below with what tripped you up, what worked differently, or what you'd add to this guide. The indie hacker community catches what solo writers miss.
---