AgentSkillsCN

Clerk

Clerk

SKILL.md

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

ComponentUsage
<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

ComponentUsage
<OrganizationSwitcher />Org dropdown
<CreateOrganization />Create org modal
<OrganizationProfile />Org settings
<OrganizationList />List user's orgs

User Profile

ComponentUsage
<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