Clerk Auth Setup in Under 10 Minutes (2026)
Step-by-step guide to integrate Clerk authentication in your Next.js app. Real errors, production patterns, and exact setup in under 10 minutes.
TL;DR
Clerk handles authentication so you don't have to. Install the SDK, add environment variables, wrap your app with <ClerkProvider>, protect routes, and you're live. Total time: ~8 minutes if you follow this exactly.
---
Why Clerk? (30 seconds)
Clerk abstracts away session management, token handling, and user verification. For indie hackers, this means:
---
Prerequisites
---
Step 1: Create Your Clerk App (1 minute)
1. Go to [dashboard.clerk.com](https://dashboard.clerk.com) 2. Click "Create Application" 3. Name it (e.g., "my-indie-project") 4. Select your preferred sign-in methods (Google + email is standard) 5. Hit "Create Application"
You'll land on a dashboard showing your API Keys. Keep this tab open.
---
Step 2: Install & Configure (2 minutes)
```bash npm install @clerk/nextjs ```
Verify the version: [npm @clerk/nextjs](https://www.npmjs.com/package/@clerk/nextjs) shows current version (verify in official docs for breaking changes).
Create .env.local in your project root:
```env NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_your_publishable_key_here CLERK_SECRET_KEY=sk_test_your_secret_key_here 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 ```
Important: Copy these from your Clerk dashboard. The NEXT_PUBLIC_ prefix means these are safe to expose (they're not secrets).
---
Step 3: Wrap Your App (1 minute)
Update app/layout.tsx:
```typescript import { ClerkProvider } from '@clerk/nextjs';
export const metadata = { title: 'My App', };
export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <ClerkProvider> <html lang="en"> <body>{children}</body> </html> </ClerkProvider> ); } ```
This provider injects Clerk's session context everywhere. Without it, you'll see:
``` Error: <SignIn /> must be used within <ClerkProvider> ```
---
Step 4: Create Auth Pages (2 minutes)
Clerk provides pre-built components. Create app/sign-in/[[...index]]/page.tsx:
```typescript import { SignIn } from '@clerk/nextjs';
export default function SignInPage() { return ( <div style={{ display: 'flex', justifyContent: 'center', paddingTop: '60px' }}> <SignIn /> </div> ); } ```
And app/sign-up/[[...index]]/page.tsx:
```typescript import { SignUp } from '@clerk/nextjs';
export default function SignUpPage() { return ( <div style={{ display: 'flex', justifyContent: 'center', paddingTop: '60px' }}> <SignUp /> </div> ); } ```
The [[...index]] pattern is Next.js magic for catch-all routes. Clerk routes requests internally.
---
Step 5: Protect Routes (2 minutes)
Create middleware.ts at project root:
```typescript import { authMiddleware } from '@clerk/nextjs';
export default authMiddleware({ publicRoutes: ['/', '/api/webhooks/clerk'], });
export const config = { matcher: [ '/((?!_next|.*\\.png|.*\\.jpg|.*\\.jpeg|.*\\.gif|.*\\.svg|api/webhooks).*)', ], }; ```
This pattern says: Everything needs auth except / and the webhook endpoint. When an unauthenticated user hits /dashboard, they're redirected to NEXT_PUBLIC_CLERK_SIGN_IN_URL.
Common error if routes aren't protected:
``` Error: currentUser() can only be used in Server Components ```
If you see this in a Client Component, move your auth logic to a Server Component parent.
---
Step 6: Access User Data (1 minute)
In any Server Component:
```typescript import { currentUser } from '@clerk/nextjs/server';
export default async function DashboardPage() { const user = await currentUser();
if (!user) { return <div>Not authenticated</div>; }
return ( <div> <h1>Welcome, {user.firstName}</h1> <p>Email: {user.emailAddresses[0].emailAddress}</p> </div> ); } ```
In Client Components, use the hook:
```typescript 'use client';
import { useUser } from '@clerk/nextjs';
export default function UserCard() { const { user } = useUser();
return ( <div> {user ? ( <p>Hello, {user.firstName}</p> ) : ( <p>Not signed in</p> )} </div> ); } ```
---
Step 7: Test It
```bash npm run dev ```
Visit http://localhost:3000. You should see your homepage. Click a protected route like /dashboard. You'll be redirected to /sign-in. Sign up with an email, and boom—you're authenticated.
Common issue:
``` Error: Invalid or expired Clerk API key ```
Double-check your .env.local keys match your Clerk dashboard exactly.
---
Production Checklist
✅ Replace test keys with production keys from Clerk dashboard ✅ Set redirect URLs in Clerk settings to your production domain ✅ Enable [HTTPS only](https://clerk.com/docs/deployments/clerk-settings) in Clerk settings ✅ Test OAuth flow (Google/GitHub) with real accounts ✅ See [webhook documentation](https://clerk.com/docs/webhooks/overview) if you need user lifecycle events
---
Going Deeper
For custom branding or complex flows, check [Clerk authentication components](https://clerk.com/docs/components/overview) docs. For building your own auth UI from scratch, use the [useAuth hook](https://clerk.com/docs/references/react/use-auth).
Also explore [role-based access control](/?guide=rbac) and [session management](/?guide=sessions) once your basic setup works.
---
What am I missing?
Did you hit an error not listed here? Have a faster setup? Tried integrating Clerk with specific frameworks (Remix, SvelteKit)? Drop it in the comments—indie hackers learn from real war stories, not marketing docs.