Clerk Auth Setup in Under 10 Minutes - 2026 Guide
Production-ready Clerk authentication setup with real error fixes, exact code patterns, and verified configuration steps for indie hackers.
TL;DR
Clerk handles auth complexity so you don't—email, OAuth, MFA in one dashboard. Install @clerk/nextjs, add three env vars, wrap your app with <ClerkProvider>, protect routes with middleware. Real errors and fixes included below.
---
Why Clerk?
Auth is a security minefield. Clerk abstracts away password hashing, session management, and OAuth flows. For indie projects, this means:
<SignIn />, <UserProfile /> just work---
Step 1: Create a Clerk Project (2 minutes)
1. Visit [clerk.com](https://clerk.com) and sign up 2. Create a new application — select your framework (Next.js shown below) 3. Copy your API Key and Publishable Key from the dashboard
You'll see a screen like this after creation. Keep this tab open.
---
Step 2: Install and Configure (3 minutes)
Install the package
```bash npm install @clerk/nextjs ```
Verify version in your package.json. As of 2026, the latest is @clerk/nextjs@5.x — verify in official docs for current version.
Set environment variables
Create .env.local:
```env NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxx CLERK_SECRET_KEY=sk_test_xxxxxxxxxxxx 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=/dashboard ```
Copy values from your Clerk dashboard.
Wrap your app with ClerkProvider
In app/layout.tsx (Next.js 13+):
```typescript import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <ClerkProvider> <html lang="en"> <body>{children}</body> </html> </ClerkProvider> ); } ```
Common error #1:
```
Error: ClerkProvider must wrap your application
```
Fix: Ensure <ClerkProvider> wraps {children} at the root layout level.
---
Step 3: Add Auth Routes (2 minutes)
Create sign-in and sign-up pages. In app/sign-in/[[...sign-in]]/page.tsx:
```typescript import { SignIn } from '@clerk/nextjs';
export default function SignInPage() { return ( <div className="flex items-center justify-center min-h-screen"> <SignIn /> </div> ); } ```
In app/sign-up/[[...sign-up]]/page.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 [[...sign-in]] catch-all routing handles Clerk's internal OAuth callbacks automatically.
---
Step 4: Protect Routes with Middleware (2 minutes)
Create middleware.ts at your project root:
```typescript import { authMiddleware } from '@clerk/nextjs';
export default authMiddleware({ publicRoutes: ['/'], ignoredRoutes: ['/api/webhooks/clerk'], });
export const config = { matcher: [ '/((?!_next|static|favicon.ico).*)', ], }; ```
This redirects unauthenticated users to /sign-in except on public routes. Adjust publicRoutes for your app.
Common error #2:
```
Error: Auth middleware did not find publishable key
```
Fix: Restart your dev server after adding .env.local. Environment variables aren't hot-reloaded.
---
Step 5: Access User Data (1 minute)
In any Client Component, use useUser():
```typescript 'use client';
import { useUser, UserButton } from '@clerk/nextjs';
export function Dashboard() { const { user, isLoaded } = useUser();
if (!isLoaded) return <div>Loading...</div>;
return ( <div> <h1>Welcome, {user?.firstName}</h1> <UserButton afterSignOutUrl="/" /> </div> ); } ```
For Server Components, use auth():
```typescript import { auth } from '@clerk/nextjs';
export async function ProfileHeader() { const { userId } = auth(); if (!userId) { return <div>Not authenticated</div>; }
return <div>User ID: {userId}</div>; } ```
---
Production Checklist
CLERK_SECRET_KEY in production deployment (never commit to git)NEXT_PUBLIC_CLERK_SIGN_IN_URL to your production domainCommon error #3:
```
Error: Invalid Clerk API key format
```
Fix: Ensure your key starts with pk_test_ (development) or pk_live_ (production). Copy-paste directly from dashboard to avoid typos.
---
Next Steps
<ClerkProvider appearance={{...}}>---
Resources
---
What am I missing?
This covers the happy path. What did you hit?
Drop corrections and additions in the comments. Senior developers catching inaccuracies keeps this accurate.