Skip to main content

clerk-auth

Expert patterns for Clerk auth implementation, middleware, organizations, webhooks, and user sync

Jump to install

Source facts

Repository
muhammedadnank/Antigravity-Skills
Last source activity
June 6, 2026 at 18:30
Detected SKILL.md language
English
Stars
13
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
clerk-auth
description
Expert patterns for Clerk auth implementation, middleware, organizations, webhooks, and user sync
risk
safe
source
vibeship-spawner-skills (Apache 2.0)
date_added
2026-02-27T00:00:00.000Z
# Clerk Authentication Expert patterns for Clerk auth implementation, middleware, organizations, webhooks, and user sync ## Patterns ### Next.js App Router Setup Complete Clerk setup for Next.js 14/15 App Router. Includes ClerkProvider, environment variables, and basic sign-in/sign-up components. Key components: - ClerkProvider: Wraps app for auth context - <SignIn />, <SignUp />: Pre-built auth forms - <UserButton />: User menu with session management ### Code_example # Environment variables (.env.local) NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... 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=/onboarding // 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> ); } // app/sign-in/[[...sign-in]]/page.tsx import { SignIn } from '@clerk/nextjs'; export default function SignInPage() { return ( <div className="flex justify-center items-center min-h-screen"> <SignIn /> </div> ); } // app/sign-up/[[...sign-up]]/page.tsx import { SignUp } from '@clerk/nextjs'; export default function SignUpPage() { return ( <div className="flex justify-center items-center min-h-screen"> <SignUp /> </div> ); } // components/Header.tsx import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/nextjs'; export function Header() { return ( <header className="flex justify-between p-4"> <h1>My App</h1> <SignedOut> <SignInButton /> </SignedOut> <SignedIn> <UserButton afterSignOutUrl="/" /> </SignedIn> </header> ); } ### Anti_patterns - Pattern: ClerkProvider inside page component | Why: Provider must wrap entire app in root layout | Fix: Move ClerkProvider to app/layout.tsx - Pattern: Using auth() without middleware | Why: auth() requires clerkMiddleware to be configured | Fix: Set up middleware.ts with clerkMiddleware ### References - https://clerk.com/docs/nextjs/getting-started/quickstart ### Middleware Route Protection Protect routes using clerkMiddleware and createRouteMatcher. Best practices: - Single middleware.ts file at project root - Use createRouteMatcher for route groups - auth.protect() for explicit protection - Centralize all auth logic in middleware ### Code_example // middleware.ts import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; // Define protected route patterns const isProtectedRoute = createRouteMatcher([ '/dashboard(.*)', '/settings(.*)', '/api/private(.*)', ]); // Define public routes (optional, for clarity) const isPublicRoute = createRouteMatcher([ '/', '/sign-in(.*)', '/sign-up(.*)', '/api/webhooks(.*)', ]); export default clerkMiddleware(async (auth, req) => { // Protect matched routes if (isProtectedRoute(req)) { await auth.protect(); } }); export const config = { matcher: [ // Match all routes except static files '/((?!_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)(.*)', ], }; // Advanced: Role-based protection export default clerkMiddleware(async (auth, req) => { if (isProtectedRoute(req)) { await auth.protect(); } // Admin routes require admin role if (req.nextUrl.pathname.startsWith('/admin')) { await auth.protect({ role: 'org:admin', }); } // Premium routes require premium permission if (req.nextUrl.pathname.startsWith('/premium')) { await auth.protect({ permission: 'org:premium:access', }); } }); ### Anti_patterns - Pattern: Multiple middleware.ts files | Why: Causes conflicts and redirect loops | Fix: Use single middleware.ts with route matchers - Pattern: Manual redirects in components | Why: Double redirects, missed routes | Fix: Handle all redirects in middleware - Pattern: Missing matcher config | Why: Middleware won't run on all routes | Fix: Add comprehensive matcher pattern ### References - https://clerk.com/docs/reference/nextjs/clerk-middleware ### Server Component Authentication Access auth state in Server Components using auth() and currentUser(). Key functions: - auth(): Returns userId, sessionId, orgId, claims - currentUser(): Returns full User object - Both require clerkMiddleware to be configured ### Code_example // app/dashboard/page.tsx (Server Component) import { auth, currentUser } from '@clerk/nextjs/server'; import { redirect } from 'next/navigation'; export default async function DashboardPage() { const { userId } = await auth(); if (!userId) { redirect('/sign-in'); } // Full user data (counts toward rate limits) const user = await currentUser(); return ( <div> <h1>Welcome, {user?.firstName}!</h1> <p>Email: {user?.emailAddresses[0]?.emailAddress}</p> </div> ); } // Using auth() for quick checks export default async function ProtectedLayout({ children, }: { children: React.ReactNode; }) { const { userId, orgId, orgRole } = await auth(); if (!userId) { redirect('/sign-in'); } // Check organization access if (!orgId) { redirect('/select-org'); } return ( <div> <p>Organization Role: {orgRole}</p> {children} </div> ); } // Server Action with auth check // app/actions/posts.ts 'use server'; import { auth } from '@clerk/nextjs/server'; export async function createPost(formData: FormData) { const { userId } = await auth(); if (!userId) { throw new Error('Unauthorized'); } const title = formData.get('title') as string; // Create post with userId const post = await prisma.post.create({ data: { title, authorId: userId, }, }); return post; } ### Anti_patterns - Pattern: Not awaiting auth() | Why: auth() is async in App Router | Fix: Use await auth() or const { userId } = await auth() - Pattern: Using currentUser() for simple checks | Why: Counts toward rate limits, slower than auth() | Fix: Use auth() for userId checks, currentUser() for user data ### References - https://clerk.com/docs/references/nextjs/auth ### Client Component Hooks Access auth state in Client Components using hooks. Key hooks: - useUser(): User object and loading state - useAuth(): Auth state, signOut, etc. - useSession(): Session object - useOrganization(): Current organization ### Code_example // components/UserProfile.tsx 'use client'; import { useUser, useAuth } from '@clerk/nextjs'; export function UserProfile() { const { user, isLoaded, isSignedIn } = useUser(); const { signOut } = useAuth(); if (!isLoaded) { return <div>Loading...</div>; } if (!isSignedIn) { return <div>Not signed in</div>; } return ( <div> <img src={user.imageUrl} alt={user.fullName ?? ''} /> <h2>{user.fullName}</h2> <p>{user.emailAddresses[0]?.emailAddress}</p> <button onClick={() => signOut()}>Sign Out</button> </div> ); } // Organization context 'use client'; import { useOrganization, useOrganizationList } from '@clerk/nextjs'; export function OrgSwitcher() { const { organization, membership } = useOrganization(); const { setActive, userMemberships } = useOrganizationList({ userMemberships: { infinite: true }, }); if (!organization) { return <p>No organization selected</p>; } return ( <div> <p>Current: {organization.name}</p> <p>Role: {membership?.role}</p> <select onChange={(e) => setActive?.({ organization: e.target.value })} value={organization.id} > {userMemberships.data?.map((mem) => ( <option key={mem.organization.id} value={mem.organization.id}> {mem.organization.name} </option> ))} </select> </div> ); } // Protected client component 'use client'; import { useAuth } from '@clerk/nextjs'; import { useRouter } from 'next/navigation'; import { useEffect } from 'react'; export function ProtectedContent() { const { isLoaded, userId } = useAuth(); const router = useRouter(); useEffect(() => { if (isLoaded && !userId) { router.push('/sign-in'); } }, [isLoaded, userId, router]); if (!isLoaded || !userId) { return <div>Loading...</div>; } return <div>Protected content here</div>; } ### Anti_patterns - Pattern: Not checking isLoaded | Why: Auth state undefined during hydration | Fix: Always check isLoaded before accessing user/auth state - Pattern: Using hooks in Server Components | Why: Hooks only work in Client Components | Fix: Use auth() and currentUser() in Server Components ### References - https://clerk.com/docs/references/react/use-user ### Organizations and Multi-Tenancy Implement B2B multi-tenancy with Clerk Organizations. Features: - Multiple orgs per user - Roles and permissions - Organization-scoped data - Enterprise SSO per organization ### Code_example // Organization creation UI // app/create-org/page.tsx import { CreateOrganization } from '@clerk/nextjs'; export default function CreateOrgPage() { return ( <div className="flex justify-center"> <CreateOrganization afterCreateOrganizationUrl="/dashboard" /> </div> ); } // Organization profile and management // app/org-settings/page.tsx import { OrganizationProfile } from '@clerk/nextjs'; export default function OrgSettingsPage() { return <OrganizationProfile />; } // Organization switcher in header // components/Header.tsx import { OrganizationSwitcher, UserButton } from '@clerk/nextjs'; export function Header() { return ( <header className="flex justify-between p-4"> <OrganizationSwitcher hidePersonal afterCreateOrganizationUrl="/dashboard" afterSelectOrganizationUrl="/dashboard" /> <UserButton /> </header> ); } // Org-scoped data access // app/dashboard/page.tsx import { auth } from '@clerk/nextjs/server'; import { prisma } from '@/lib/prisma'; export default async function DashboardPage() { const { orgId } = await auth(); if (!orgId) { redirect('/select-org'); } // Fetch org-scoped data const projects = await prisma.project.findMany({ where: { organizationId: orgId }, }); return ( <div> <h1>Projects</h1> {projects.map((p) => ( <div key={p.id}>{p.name}</div> ))} </div> ); } // Role-based UI 'use client';
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub