Agent Native

clerk-nextjs-patterns

This skill teaches advanced Next.js patterns for protecting routes, Server Actions, and API endpoints with Clerk authentication.

by ClerkOfficial publisherNot reviewed

Identity proven by GitHub's verified organisation badge (clerk)

Agents
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode
License
MIT (permissive)
Pin
a02dbd2pinned Oct 9, 2026
Context cost
about 2,100 tokenswhen the skill loads
GitHub stars
86on clerk/skills
Scripts
1 script

What it does

“Advanced Next.js patterns - middleware, Server Actions, caching with Clerk.”
The author's description, from SKILL.md

Generated summary from the skill's own text

This skill teaches advanced Next.js patterns for protecting routes, Server Actions, and API endpoints with Clerk authentication. It covers server vs client authentication APIs, auth caching strategies, and session token handling for external APIs.

When to use it

  • Protecting Next.js pages, Route Handlers, and Server Actions with auth checks
  • Implementing conditional rendering based on user authentication or permissions
  • Passing custom JWT tokens to third-party services like Hasura or Supabase
  • Caching authenticated data without mixing users' information together

When not to

  • Initial Clerk setup or installation, see clerk-setup skill instead
  • B2B organization patterns or role/permission gating, see clerk-orgs skill instead

Install

These commands install from the publisher's repository and need no account.

skills CLI

npx skills add clerk/skills --skill clerk-nextjs-patterns

Installs from the repository's default branch, which may have changed since the commit we checked.

GitHub CLI 2.90 or later

gh skill install clerk/skills clerk-nextjs-patterns@a02dbd2a933b8929525129a6cb9dcf6ed66d0adf

Installs the commit we checked (a02dbd2).

Copy by hand

Download the folder at a02dbd2 and put it in .claude/skills/clerk-nextjs-patterns for Claude Code and in .agents/skills/clerk-nextjs-patterns for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.

Skill folder on GitHub at a02dbd2

Copy for your agent

A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.

Prompt

Install the agent skill "clerk-nextjs-patterns" by Clerk.

Source, pinned to commit a02dbd2a933b8929525129a6cb9dcf6ed66d0adf:
https://github.com/clerk/skills/tree/a02dbd2a933b8929525129a6cb9dcf6ed66d0adf/skills/clerk-nextjs-patterns

1. Download that folder at exactly this commit. Do not use the default branch.
2. Copy every file unchanged, keeping the folder structure, into .claude/skills/clerk-nextjs-patterns/ for Claude Code and .agents/skills/clerk-nextjs-patterns/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist.
3. Do not edit the files, and do not run any script in the folder unless I ask you to.
4. When you are done, list the files you copied and where you put them.

Listing and checks: https://www.agentnative.dev/skills/clerk/clerk-nextjs-patterns

SKILL.md at the pin

---
name: clerk-nextjs-patterns
description: Advanced Next.js patterns - middleware, Server Actions, caching with
  Clerk.
license: MIT
allowed-tools: WebFetch
compatibility: Requires NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY. For manual JWT verification (standalone API servers without Clerk middleware), additionally requires CLERK_JWT_KEY or CLERK_PEM_PUBLIC_KEY.
metadata:
  author: clerk
  version: 2.2.0
---

# 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.

> **Route protection:** call `await auth.protect()` in each page, Route Handler, and Server Function. Keep `clerkMiddleware()` bare. `createRouteMatcher` is deprecated in `@clerk/nextjs` 7 and will be removed in the next major version. Don't add it. If a project already uses it, leave it working and point the user to the [migration guide](https://clerk.com/docs/guides/development/upgrading/upgrade-guides/migrate-from-create-route-matcher).

## What Do You Need?

| Task | Reference |
|------|-----------|
| Server vs client auth (`auth()` vs hooks) | references/server-vs-client.md |
| Protect routes (check in each page, handler, and Server Function) | references/middleware-strategies.md |
| Protect Server Actions | references/server-actions.md |
| API route auth (401 vs 403) | references/api-routes.md |
| Cache auth data (user-scoped caching) | references/caching-auth.md |

## References

| Reference | Description |
|-----------|-------------|
| `references/server-vs-client.md` | `await auth()` vs hooks |
| `references/middleware-strategies.md` | `auth.protect()` in each resource, bare `clerkMiddleware()` in `proxy.ts` (Next.js <=15: `middleware.ts`) |
| `references/server-actions.md` | Protect mutations |
| `references/api-routes.md` | 401 vs 403 |
| `references/caching-auth.md` | User-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

```typescript
// 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:**

```tsx
import { Show } from '@clerk/nextjs'

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

**Authorization checks (B2B):**

```tsx
// 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:**

```tsx
<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

| Symptom | Cause | Fix |
|---------|-------|-----|
| `undefined` userId in Server Component | Missing `await` | `await auth()` not `auth()` |
| Auth not working on API routes | Missing matcher | Add `'/(api|trpc)(.*)'` to `proxy.ts` (Next.js <=15: `middleware.ts`) |
| Cache returns wrong user's data | Missing userId in key | Include `userId` in `unstable_cache` key |
| Mutations bypass auth | Unprotected Server Action | Check `auth()` at start of action |
| Protected page returns 200, not 307, while signed out | The redirect happened after streaming started (e.g. under a `loading.tsx`) | Confirm the body redirects to sign-in and has no protected data. Keep the check in the page |
| Wrong HTTP error code | Confused 401/403 | 401 = 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)**:

```typescript
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)**:

```typescript
'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:

```typescript
'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):

```typescript
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`):

```typescript
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](https://clerk.com/docs/reference/nextjs/overview)

From clerk/skills at a02dbd2, shown as plain text under its license: MIT (permissive). The license text is in skills/clerk-nextjs-patterns/SKILL.md.

View SKILL.md on GitHub at a02dbd2

Files

12 files, 30 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.

Skill folder on GitHub at a02dbd2
All 21 skills from Clerk

More from Clerk

  • audit-clerk-skill

    Audits the bundled clerk-cli skill against the Clerk CLI source repository, comparing commands, flags, and agent-mode behavior to detect drift, gaps, and…

    Claude Code · about 1.9K tokens · pinned 6 days ago

  • audit-expo-skill

    Audits the clerk-expo skill against the @clerk/expo SDK source and clerk-docs to detect API drift, missing coverage, and stale claims.

    Claude Code · about 2.2K tokens · pinned 6 days ago

  • clerk

    This skill routes Clerk authentication tasks to specialized sub-skills based on the user's specific need, such as framework setup, CLI operations, custom UI…

    Claude Code, Codex, Cursor +4 · about 1.4K tokens · pinned 6 days ago

  • clerk-android

    This skill implements Clerk authentication in native Android apps written in Kotlin and Jetpack Compose by following the clerk-android SDK patterns and…

    Claude Code, Codex, Cursor +4 · about 2K tokens · pinned 6 days ago

  • clerk-astro-patterns

    This skill teaches the agent to integrate Clerk authentication into Astro applications using middleware, SSR pages, island components, and API routes.

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 850 tokens · pinned 6 days ago

  • clerk-billing

    Clerk Billing enables subscription management in SaaS apps by rendering pricing tables and checkout drawers, configuring plans with feature entitlements, and…

    Claude Code, Codex, Cursor +4 · about 4.9K tokens · pinned 6 days ago

All Backend and APIs skills
  • supabase

    This skill enables the agent to work with Supabase products including Database, Auth, Edge Functions, Realtime, Storage, and Vectors.

    by SupabaseOfficial publisher

    Claude Code, Codex, Cursor +4 · about 3.3K tokens · pinned 6 days ago

  • auth

    This skill guides implementing user authentication in Next.js applications using Clerk, Better Auth, Descope, Auth0, or Vercel's own identity primitives like…

    by VercelOfficial publisher

    Claude Code, Codex, Cursor +3 · about 5K tokens · pinned 6 days ago

  • flags-sdk

    The Flags SDK is a feature flags toolkit for Next.js and SvelteKit that declares flags as functions and evaluates them server-side.

    by VercelOfficial publisher

    Claude Code, Codex, Cursor +3 · about 5.8K tokens · pinned 6 days ago

  • queues

    Vercel Queues enables agents to publish JSON messages to durable topics using @vercel/queue (JavaScript) or vercel-queue (Python), with consumer functions…

    by VercelOfficial publisher

    Claude Code, Codex, Cursor +3 · about 1.6K tokens · pinned 6 days ago

  • neon-functions

    Neon Functions deploys long-running Node.js HTTP handlers to your Neon database branch, injecting DATABASE_URL and hosting stateful workloads like AI agents…

    by NeonOfficial publisher

    Claude Code, Codex, Cursor +4 · about 11.9K tokens · pinned 6 days ago

  • clerk-nuxt-patterns

    This skill teaches agents how to implement authentication in Nuxt 3 applications using the Clerk SDK.

    by ClerkOfficial publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 710 tokens · pinned 6 days ago

The skills listed here are third-party software, collected from public GitHub repositories as a catalog of recent skills and provided as is. Agent Native does not write or control them and is not responsible for any harm, loss or damage caused by installing or using them. Labels and checks describe what we saw at one commit and are not a guarantee. Read a skill's files before you run it. Terms of use