Awesome-claude-skills integrating-clerk-nextjs
Integrates Clerk authentication into Next.js App Router applications following current best practices. Use when the user asks to "add Clerk", "integrate Clerk", "set up Clerk authentication", "add user authentication", or mentions Clerk setup in Next.js App Router projects.
git clone https://github.com/asvskartheek/awesome-claude-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/asvskartheek/awesome-claude-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/integrating-clerk-nextjs" ~/.claude/skills/asvskartheek-awesome-claude-skills-integrating-clerk-nextjs && rm -rf "$T"
integrating-clerk-nextjs/SKILL.mdIntegrating Clerk with Next.js App Router
Enforces current and correct instructions for integrating Clerk into Next.js App Router applications.
Quick Start
For a typical integration:
- Install
@clerk/nextjs@latest - Set up environment variables in
.env.local - Create
withmiddleware.tsclerkMiddleware() - Wrap app with
in<ClerkProvider>app/layout.tsx - Add Clerk components (
,<SignInButton>
, etc.)<UserButton>
Complete Integration Workflow
Step 1: Install Clerk SDK
npm install @clerk/nextjs
Step 2: Configure Environment Variables
Action: Create or update
.env.local with Clerk API keys.
CRITICAL:
- NEVER write actual keys to tracked files
- ONLY use placeholder values in code examples
- Verify
excludes.gitignore
files.env*
# .env.local NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY CLERK_SECRET_KEY=YOUR_SECRET_KEY
Get keys from: Clerk Dashboard → API Keys
Step 3: Create Middleware
File:
middleware.ts (at project root, or in src/ if using src directory)
// middleware.ts import { clerkMiddleware } from "@clerk/nextjs/server"; export default clerkMiddleware(); export const config = { matcher: [ // Skip Next.js internals and all static files, unless found in search params "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", // Always run for API routes "/(api|trpc)(.*)", ], };
CRITICAL: Use
clerkMiddleware() from @clerk/nextjs/server - NOT authMiddleware() (deprecated).
Step 4: Wrap App with ClerkProvider
File:
app/layout.tsx
Before editing:
- Read the existing
fileapp/layout.tsx - Identify the current structure
- Preserve existing imports and metadata
Add these imports:
import { ClerkProvider, SignInButton, SignUpButton, SignedIn, SignedOut, UserButton, } from "@clerk/nextjs";
Wrap the app:
export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <ClerkProvider> <html lang="en"> <body> <header> <SignedOut> <SignInButton /> <SignUpButton /> </SignedOut> <SignedIn> <UserButton /> </SignedIn> </header> {children} </body> </html> </ClerkProvider> ); }
Step 5: Verify Implementation
Run through this checklist before completing:
- Middleware:
used inclerkMiddleware()middleware.ts - Layout:
wraps app in<ClerkProvider>app/layout.tsx - Imports: References from
or@clerk/nextjs@clerk/nextjs/server - App Router: Using
directory (notapp/
orpages/
)_app.tsx - Environment Variables: Only placeholders in code, actual keys in
.env.local - File Security:
in.env*.gitignore
Step 6: Test the Integration
npm run dev
Navigate to the app and verify:
- Sign-in/sign-up buttons appear for unauthenticated users
- User button appears for authenticated users
- Authentication flow works end-to-end
Server-Side Authentication
When accessing authentication data in Server Components or API routes:
import { auth } from "@clerk/nextjs/server"; export default async function Page() { const { userId } = await auth(); if (!userId) { return <div>Not authenticated</div>; } return <div>User ID: {userId}</div>; }
CRITICAL:
- Import
fromauth@clerk/nextjs/server - Always use
(it's async)await auth()
Protected Routes
To protect specific routes, update
middleware.ts:
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server"; const isProtectedRoute = createRouteMatcher([ '/dashboard(.*)', '/profile(.*)', ]); export default clerkMiddleware(async (auth, req) => { if (isProtectedRoute(req)) { 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)(.*)", ], };
CRITICAL: What NOT to Do
NEVER do the following:
- ❌ Use
- it's deprecated, useauthMiddleware()clerkMiddleware() - ❌ Reference
or_app.tsx
directory for Clerk setuppages/ - ❌ Import from deprecated APIs (
, oldwithAuth
)currentUser - ❌ Write actual API keys to tracked files
- ❌ Use sync
- it's async, always useauth()await auth() - ❌ Forget to wrap with
in layout<ClerkProvider>
See reference/outdated-patterns.md for detailed examples of deprecated approaches.
Troubleshooting
Issue: "clerkMiddleware is not a function"
- Cause: Using old version of
@clerk/nextjs - Fix: Run
npm install @clerk/nextjs@latest
Issue: Authentication not working
- Check: Is
at the root (or inmiddleware.ts
if using src directory)?src/ - Check: Are environment variables set correctly in
?.env.local - Check: Is the app wrapped with
?<ClerkProvider>
Issue: TypeScript errors with
auth()
- Cause: Not awaiting async
functionauth() - Fix: Change
toconst { userId } = auth()const { userId } = await auth()
Best Practices
- Environment Variables: Always use
for local development.env.local - Type Safety: Use TypeScript for better autocomplete and type checking
- Protected Routes: Use
for route-based protectioncreateRouteMatcher - Server Components: Leverage
for server-side authentication checksauth() - Error Handling: Wrap authentication calls in try-catch for production apps
Additional Resources
Example Implementation
Here's a complete minimal example:
middleware.ts:
import { clerkMiddleware } from "@clerk/nextjs/server"; export default clerkMiddleware(); 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)(.*)", ], };
app/layout.tsx:
import { ClerkProvider, SignInButton, SignedIn, SignedOut, UserButton } from "@clerk/nextjs"; import "./globals.css"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <ClerkProvider> <html lang="en"> <body> <header> <SignedOut> <SignInButton /> </SignedOut> <SignedIn> <UserButton /> </SignedIn> </header> <main>{children}</main> </body> </html> </ClerkProvider> ); }
app/page.tsx:
import { auth } from "@clerk/nextjs/server"; export default async function Home() { const { userId } = await auth(); return ( <div> <h1>Welcome {userId ? "back" : "to our app"}!</h1> </div> ); }
.env.local:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_...