clerk-nextjs-patterns

bởi clerk

Các mẫu Next.js nâng cao cho xác thực, middleware, Server Actions và bộ nhớ đệm theo phạm vi người dùng với Clerk. Phân biệt auth() phía máy chủ với hook useAuth() phía máy khách; việc trộn lẫn chúng là một lỗi phổ biến gây hỏng. Bao gồm các chiến lược middleware (ưu tiên công khai so với ưu tiên bảo vệ), bảo vệ tuyến API và mã trạng thái HTTP phù hợp (401 so với 403). Bao gồm các mẫu bộ nhớ đệm theo phạm vi người dùng với unstable_cache và bảo vệ Server Actions khỏi các thay đổi trái phép. Cung cấp khả năng tương thích Core 2...

npx skills add https://github.com/clerk/skills --skill clerk-nextjs-patterns

Next.js Patterns

Version: Check package.json for the SDK version — see clerk skill for the version table. Core 2 differences are noted inline with > **Core 2 ONLY (skip if current SDK):** callouts.

For basic setup, see clerk-setup skill.

What Do You Need?

TaskReference
Server vs client auth (auth() vs hooks)references/server-vs-client.md
Configure middleware (public-first vs protected-first)references/middleware-strategies.md
Protect Server Actionsreferences/server-actions.md
API route auth (401 vs 403)references/api-routes.md
Cache auth data (user-scoped caching)references/caching-auth.md

References

ReferenceDescription
references/server-vs-client.mdawait auth() vs hooks
references/middleware-strategies.mdPublic-first vs protected-first, proxy.ts (Next.js <=15: middleware.ts)
references/server-actions.mdProtect mutations
references/api-routes.md401 vs 403
references/caching-auth.mdUser-scoped caching

Mental Model

Server vs Client = different auth APIs:

  • Server: await auth() from @clerk/nextjs/server (async!)
  • Client: useAuth() hook from @clerk/nextjs (sync)

Never mix them. Server Components use server imports, Client Components use hooks.

Key properties from auth():

  • isAuthenticated — boolean, replaces the !!userId pattern
  • sessionStatus — 'active' | 'pending', for detecting incomplete session tasks
  • userId, orgId, orgSlug, has(), protect() — unchanged

Core 2 ONLY (skip if current SDK): isAuthenticated and sessionStatus are not available. Check !!userId instead.

Minimal Pattern

// Server Component
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { isAuthenticated, userId } = await auth()  // MUST await!
  if (!isAuthenticated) return <p>Not signed in</p>
  return <p>Hello {userId}</p>
}

Core 2 ONLY (skip if current SDK): isAuthenticated is not available. Use if (!userId) instead.

Conditional Rendering with <Show>

For client-side conditional rendering based on auth state. <Show> covers both authentication checks and authorization (feature, plan, role, permission) in one component.

Authentication check:

import { Show } from '@clerk/nextjs'

<Show when="signed-in" fallback={<p>Please sign in</p>}>
  <Dashboard />
</Show>

Authorization checks (B2B):

// Feature-based (preferred — features can move between plans without redeploy)
<Show when={{ feature: 'analytics' }} fallback={<UpgradePrompt />}>
  <AnalyticsDashboard />
</Show>

// Permission-based (preferred over role-based for granular access)
<Show when={{ permission: 'org:invoices:create' }}>
  <NewInvoiceButton />
</Show>

// Plan-based (tier-level gating)
<Show when={{ plan: 'pro' }}>
  <ProFeatures />
</Show>

// Role-based (use sparingly — prefer permission)
<Show when={{ role: 'org:admin' }}>
  <AdminPanel />
</Show>

Callback for complex logic:

<Show when={(has) => has({ role: 'org:admin' }) || has({ role: 'org:billing_manager' })}>
  <BillingActions />
</Show>

Core 2 ONLY (skip if current SDK): <Show> does not exist. For authentication, use <SignedIn> and <SignedOut>. For authorization (role / permission), use <Protect> with the same prop names (role, permission, condition). Feature- and plan-based variants require Core 3. See clerk-custom-ui skill, core-3/show-component.md for the full migration table.

Common Pitfalls

SymptomCauseFix
undefined userId in Server ComponentMissing awaitawait auth() not auth()
Auth not working on API routesMissing matcherAdd `'/(api
Cache returns wrong user's dataMissing userId in keyInclude userId in unstable_cache key
Mutations bypass authUnprotected Server ActionCheck auth() at start of action
Wrong HTTP error codeConfused 401/403401 = not signed in, 403 = no permission

Session Tokens & Custom JWTs

getToken() for external APIs

Pass a custom JWT to third-party services (Hasura, Supabase, etc.) using JWT templates defined in the Clerk dashboard.

Server-side (Server Component or Route Handler):

import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { getToken } = await auth()
  const token = await getToken({ template: 'hasura' })
  if (!token) return <p>Not authenticated</p>

  const res = await fetch('https://api.example.com/graphql', {
    headers: { Authorization: `Bearer ${token}` },
  })
  const data = await res.json()
  return <pre>{JSON.stringify(data)}</pre>
}

Client-side (Client Component):

'use client'
import { useAuth } from '@clerk/nextjs'

export function DataFetcher() {
  const { getToken } = useAuth()

  async function fetchData() {
    const token = await getToken({ template: 'supabase' })
    if (!token) return

    const res = await fetch('https://api.example.com/data', {
      headers: { Authorization: `Bearer ${token}` },
    })
    return res.json()
  }

  return <button onClick={fetchData}>Fetch</button>
}

getToken() returns null when the user is not authenticated — always null-check before use.

useSession() for session data

Access session metadata in client components:

'use client'
import { useSession } from '@clerk/nextjs'

export function SessionInfo() {
  const { session } = useSession()
  if (!session) return null

  return (
    <p>
      Session {session.id} — last active: {session.lastActiveAt.toISOString()}
    </p>
  )
}

Manual JWT verification (no Clerk middleware)

For standalone API servers that receive Clerk session tokens from the Authorization header or the __session cookie (same-origin).

Using @clerk/backend verifyToken (recommended):

import { verifyToken } from '@clerk/backend'

const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return res.status(401).json({ error: 'No token' })

try {
  const claims = await verifyToken(token, {
    jwtKey: process.env.CLERK_JWT_KEY,
  })
  // claims.sub = userId
} catch {
  return res.status(401).json({ error: 'Invalid token' })
}

Using jsonwebtoken (when you can't use @clerk/backend):

import jwt from 'jsonwebtoken'

const publicKey = process.env.CLERK_PEM_PUBLIC_KEY!.replace(/\\n/g, '\n')
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return res.status(401).json({ error: 'No token' })

try {
  const claims = jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as jwt.JwtPayload
  // Manually check exp and nbf (jsonwebtoken does this automatically, but verify azp if needed)
  // claims.sub = userId
} catch {
  return res.status(401).json({ error: 'Invalid or expired token' })
}

Token sources:

  • Same-origin requests: __session cookie (Clerk sets this automatically)
  • Cross-origin / mobile / API-to-API: Authorization: Bearer <token> header

CRITICAL: Always check exp and nbf claims. verifyToken from @clerk/backend handles this automatically; with raw jsonwebtoken, set ignoreExpiration: false (default) and ensure clockTolerance is minimal.

See Also

  • clerk-setup - Initial Clerk install
  • clerk-orgs - B2B patterns (active org, role/permission gating)
  • clerk-billing - Plan and feature entitlements with has()
  • clerk-webhooks - Sync user/org events to your database
  • clerk-custom-ui - Theming and customization for built-in components

Docs

Next.js SDK

Thêm skills từ clerk

mosaic
clerk
Work on Mosaic UI: styling a component with slot recipes (`defineSlotRecipe` / `useRecipe` / slots / variants), or building a flow — authoring a state machine…
clerk-billing
clerk
Quản lý đăng ký Clerk Billing - hiển thị PricingTable của Clerk
clerk-expo-patterns
clerk
Các mẫu Expo / React Native với Clerk — Bộ nhớ đệm token SecureStore, OAuth
changesets
clerk
Create or refresh a `.changeset/<slug>.md` for the current branch, or report that none is required. Triggers on "/changesets create", "add a changeset",…
clerk
clerk
Tệp nhị phân clerk là một cổng đã được xác thực trước đến Backend API và Platform API của Clerk, cùng với các công cụ cấp dự án (xác thực, liên kết, kéo biến môi trường, cấu hình phiên bản). Khi người dùng hỏi bất cứ điều gì liên quan đến tài nguyên Clerk, hãy sử dụng clerk trước thay vì tự viết lệnh curl.
clerk-cli
clerk
Tệp nhị phân clerk là một cổng đã được xác thực trước đến Backend API và Platform API của Clerk, cùng với các công cụ cấp dự án (xác thực, liên kết, kéo môi trường, cấu hình phiên bản). Khi người dùng hỏi bất cứ điều gì liên quan đến tài nguyên Clerk, hãy sử dụng clerk trước thay vì tự viết lệnh curl.
clerk-astro-patterns
clerk
Các mẫu Astro với Clerk — middleware, trang SSR, thành phần island,
audit-expo-skill
clerk
Audits the bundled `clerk-expo` skill against the @clerk/expo SDK source and clerk-docs, then proposes or applies updates. Use when the user says "audit the…