Clerk Authentication
Add Clerk authentication to React/Next.js applications.
When to Use
- •User wants to add authentication to their app
- •User mentions "Clerk"
- •User wants managed authentication service
- •User needs quick setup without self-hosting
Quick Setup
1. Install
bash
npm install @clerk/nextjs
2. Set Environment Variables
Create .env.local:
env
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx CLERK_SECRET_KEY=sk_test_xxx 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
Get keys from dashboard.clerk.com.
3. Add ClerkProvider
tsx
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html lang="en">
<body>{children}</body>
</html>
</ClerkProvider>
)
}
4. Add Middleware
Create middleware.ts in project root:
tsx
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/public(.*)',
])
export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
await auth.protect()
}
})
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
}
5. Create Auth Pages
Sign In (app/sign-in/[[...sign-in]]/page.tsx):
tsx
import { SignIn } from '@clerk/nextjs'
export default function SignInPage() {
return (
<div className="min-h-screen flex items-center justify-center">
<SignIn />
</div>
)
}
Sign Up (app/sign-up/[[...sign-up]]/page.tsx):
tsx
import { SignUp } from '@clerk/nextjs'
export default function SignUpPage() {
return (
<div className="min-h-screen flex items-center justify-center">
<SignUp />
</div>
)
}
6. Add UserButton
tsx
// app/dashboard/page.tsx
import { UserButton } from '@clerk/nextjs'
export default function DashboardPage() {
return (
<div>
<header className="flex justify-between p-4 border-b">
<h1>Dashboard</h1>
<UserButton />
</header>
<main className="p-6">
Protected content
</main>
</div>
)
}
Components Reference
Authentication
| Component | Usage |
|---|---|
<SignIn /> | Sign-in form |
<SignUp /> | Registration form |
<UserButton /> | Avatar dropdown |
<SignOutButton /> | Sign out link/button |
<SignedIn> | Show content when signed in |
<SignedOut> | Show content when signed out |
Organizations
| Component | Usage |
|---|---|
<OrganizationSwitcher /> | Org dropdown |
<CreateOrganization /> | Create org modal |
<OrganizationProfile /> | Org settings |
<OrganizationList /> | List user's orgs |
User Profile
| Component | Usage |
|---|---|
<UserProfile /> | Full profile page |
<UserButton /> | Compact avatar |
Hooks
tsx
import { useAuth, useUser, useOrganization } from '@clerk/nextjs'
// Auth state
const { isLoaded, isSignedIn, userId, sessionId, signOut } = useAuth()
// User data
const { user } = useUser()
// Organization
const { organization, membership } = useOrganization()
Server-Side
In Server Components
tsx
import { auth, currentUser } from '@clerk/nextjs/server'
export default async function Page() {
const { userId } = await auth()
const user = await currentUser()
if (!userId) {
return <div>Not signed in</div>
}
return <div>Hello, {user?.firstName}</div>
}
In API Routes
tsx
// app/api/protected/route.ts
import { auth } from '@clerk/nextjs/server'
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({ userId })
}
Common Patterns
Conditional Rendering
tsx
import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/nextjs'
function Header() {
return (
<header>
<SignedOut>
<SignInButton mode="modal">
<button>Sign In</button>
</SignInButton>
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
)
}
With Organizations
tsx
import { OrganizationSwitcher, UserButton } from '@clerk/nextjs'
function Navbar() {
return (
<nav className="flex items-center gap-4">
<OrganizationSwitcher />
<UserButton />
</nav>
)
}
Protect Client Component
tsx
'use client'
import { useAuth } from '@clerk/nextjs'
import { redirect } from 'next/navigation'
export default function ProtectedPage() {
const { isLoaded, isSignedIn } = useAuth()
if (!isLoaded) return <Loading />
if (!isSignedIn) redirect('/sign-in')
return <div>Protected content</div>
}
Customization
Theme
tsx
<ClerkProvider
appearance={{
baseTheme: dark,
variables: {
colorPrimary: '#8b5cf6',
},
elements: {
card: 'shadow-xl',
},
}}
>
Localization
tsx
<ClerkProvider localization={frFR}>
Project Structure
code
app/
├── layout.tsx # ClerkProvider
├── page.tsx # Landing
├── sign-in/[[...sign-in]]/
│ └── page.tsx # <SignIn />
├── sign-up/[[...sign-up]]/
│ └── page.tsx # <SignUp />
├── dashboard/
│ └── page.tsx # Protected page
└── user-profile/[[...user-profile]]/
└── page.tsx # <UserProfile />
middleware.ts # Route protection
Troubleshooting
Middleware not protecting routes
→ Check matcher config in middleware.ts
CLERK_SECRET_KEY not found
→ Ensure .env.local exists and server was restarted
Redirect loop
→ Add sign-in/sign-up routes to isPublicRoute matcher
Links
- •Documentation: https://clerk.com/docs
- •Dashboard: https://dashboard.clerk.com
- •GitHub: https://github.com/clerk/javascript