clerk-react-router-patterns
Integrates Clerk authentication with React Router v7/v8 applications using rootAuthLoader, clerkMiddleware, and getAuth to handle auth in loaders and actions.
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 1,700 tokenswhen the skill loads
- GitHub stars
- 86on clerk/skills
- Scripts
- 3 scripts
What it does
“React Router v7/v8 patterns with Clerk — rootAuthLoader, getAuth in loaders, clerkMiddleware, protected routes, SSR user data, org switching. Triggers on: react-router auth, rootAuthLoader, getAuth loader, react-router protected route, loader authentication, SSR auth react-router, useNavigate may be used only in the context of a Router.”
Generated summary from the skill's own text
Integrates Clerk authentication with React Router v7/v8 applications using rootAuthLoader, clerkMiddleware, and getAuth to handle auth in loaders and actions. Provides patterns for protected routes, SSR user data, and organization switching with the @clerk/react-router SDK.
When to use it
- Building React Router v7/v8 apps requiring user authentication
- Protecting routes and fetching user data in loaders
- Handling org switching in B2B React Router applications
- Implementing server-side auth with SSR support
When not to
- Projects using React Router versions below v7.9
- Apps already using a different auth provider or pattern
- Building static sites without server-side rendering needs
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add clerk/skills --skill clerk-react-router-patternsInstalls 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-react-router-patterns@a02dbd2a933b8929525129a6cb9dcf6ed66d0adfInstalls the commit we checked (a02dbd2).
Copy by hand
Download the folder at a02dbd2 and put it in .claude/skills/clerk-react-router-patterns for Claude Code and in .agents/skills/clerk-react-router-patterns for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
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-react-router-patterns" by Clerk. Source, pinned to commit a02dbd2a933b8929525129a6cb9dcf6ed66d0adf: https://github.com/clerk/skills/tree/a02dbd2a933b8929525129a6cb9dcf6ed66d0adf/skills/clerk-react-router-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-react-router-patterns/ for Claude Code and .agents/skills/clerk-react-router-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-react-router-patterns
SKILL.md at the pin
---
name: clerk-react-router-patterns
description: 'React Router v7/v8 patterns with Clerk — rootAuthLoader, getAuth in loaders,
clerkMiddleware, protected routes, SSR user data, org switching. Triggers on: react-router
auth, rootAuthLoader, getAuth loader, react-router protected route, loader authentication,
SSR auth react-router, useNavigate may be used only in the context of a Router.'
license: MIT
allowed-tools: WebFetch
metadata:
author: clerk
version: 1.1.0
---
# React Router Patterns
SDK: `@clerk/react-router` v3.5+. Supports React Router v7.9+ and v8.
## What Do You Need?
| Task | Reference |
|------|-----------|
| Auth in loaders and actions | references/loaders-actions.md |
| Protected routes and redirects | references/protected-routes.md |
| SSR user data and session | references/ssr-auth.md |
## React Router v7 vs v8
Check the installed `react-router` major version before scaffolding — the config differs:
| | v7.9+ | v8+ |
|--|--|--|
| Middleware API | Opt-in: set `future: { v8_middleware: true }` in `react-router.config.ts` | Always on — do NOT set the flag (v8 removed it) |
| `ssr.noExternal` workaround (below) | Not needed | **Required** |
## Minimal Setup
### 1. vite.config.ts (v8 only — REQUIRED)
React Router v8 ships development/production conditional exports. In `react-router dev`,
Vite externalizes `@clerk/react-router` for SSR, so Node resolves the production build of
react-router while the app code gets the development build — two module instances, two
Router contexts. Every request then fails during SSR with:
```
Error: useNavigate() may be used only in the context of a <Router> component.
```
**`npm ls react-router` shows a single copy — that does NOT rule this out.** The
duplication is per export condition, not per installed copy. Do not chase duplicate
installs; add the workaround (upstream issue:
https://github.com/remix-run/react-router/issues/15232):
```ts
import { reactRouter } from '@react-router/dev/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [reactRouter()],
ssr: {
noExternal: ['@clerk/react-router'],
},
})
```
### 2. root.tsx
```tsx
import { Outlet } from 'react-router'
import { rootAuthLoader, clerkMiddleware } from '@clerk/react-router/server'
import { ClerkProvider } from '@clerk/react-router'
import type { Route } from './+types/root'
export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]
export async function loader(args: Route.LoaderArgs) {
return rootAuthLoader(args)
}
export default function App({ loaderData }: Route.ComponentProps) {
return (
<ClerkProvider loaderData={loaderData}>
<Outlet />
</ClerkProvider>
)
}
```
There is no `ClerkApp` HOC in `@clerk/react-router` (that was the `@clerk/remix` API).
Render `<ClerkProvider loaderData={loaderData}>` inside the default export and pass it
the root route's `loaderData`.
### 3. react-router.config.ts (v7 only)
```ts
import type { Config } from '@react-router/dev/config'
export default {
future: {
v8_middleware: true,
},
} satisfies Config
```
On v8, omit the `future` block entirely — the flag no longer exists.
> **Required**: `rootAuthLoader` must be called in `root.tsx`'s loader. Without it, `getAuth` throws in nested loaders.
## Mental Model
React Router v7/v8 uses a middleware + loader pipeline. Clerk plugs into both layers:
- **Middleware** (`clerkMiddleware()`) — runs on every request, attaches auth to context
- **`rootAuthLoader`** — required in `root.tsx` to pass Clerk state to the client
- **`getAuth(args)`** — called inside any loader/action to get the current user
```
Request → clerkMiddleware() → rootAuthLoader → page loader → component
↓ ↓ ↓
attaches auth injects state getAuth(args)
to context to response reads context
```
## Auth in Loaders
```tsx
import { getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/dashboard'
export async function loader(args: Route.LoaderArgs) {
const { userId } = await getAuth(args)
if (!userId) throw redirect('/sign-in')
const data = await fetchUserData(userId)
return { data }
}
```
## Auth in Actions
```tsx
import { getAuth } from '@clerk/react-router/server'
export async function action(args: Route.ActionArgs) {
const { userId, orgId } = await getAuth(args)
if (!userId) throw new Response('Unauthorized', { status: 401 })
const formData = await args.request.formData()
await saveData(userId, orgId, formData)
return redirect('/dashboard')
}
```
## Client Components
```tsx
import { useAuth, useUser } from '@clerk/react-router'
export function Profile() {
const { userId, isSignedIn } = useAuth()
const { user } = useUser()
if (!isSignedIn) return null
return <p>{user?.firstName}</p>
}
```
## Org Switching
```tsx
import { OrganizationSwitcher } from '@clerk/react-router'
export function Nav() {
return <OrganizationSwitcher afterSelectOrganizationUrl="/dashboard" />
}
```
```tsx
export async function loader(args: Route.LoaderArgs) {
const { userId, orgId } = await getAuth(args)
if (!userId) throw redirect('/sign-in')
if (!orgId) throw redirect('/select-org')
return { data: await fetchOrgData(orgId) }
}
```
## Common Pitfalls
| Symptom | Cause | Fix |
|---------|-------|-----|
| `useNavigate() may be used only in the context of a <Router>` thrown from ClerkProvider during SSR in dev (v8) | Vite dev SSR externalizes `@clerk/react-router`, which then loads react-router's production build while the app uses the development build — two Router contexts. A single copy in `npm ls` does not rule this out. | Add `ssr: { noExternal: ['@clerk/react-router'] }` to `vite.config.ts`. Do NOT downgrade to v7 |
| Build error: `ClerkApp` is not exported | `ClerkApp` does not exist in `@clerk/react-router` | Use `<ClerkProvider loaderData={loaderData}>` in root.tsx's default export |
| `clerkMiddleware() not detected` | Missing middleware (or on v7, missing `v8_middleware` future flag) | Export `middleware = [clerkMiddleware()]` from root route; on v7 also set `future: { v8_middleware: true }` |
| Unknown future flag error/warning (v8) | `v8_middleware` flag left in `react-router.config.ts` after upgrading | Remove the `future.v8_middleware` entry — middleware is always on in v8 |
| `getAuth` returns empty userId | `rootAuthLoader` not called | Call `rootAuthLoader(args)` in `root.tsx` loader |
| Infinite redirect loop | Redirect target is also protected | Exclude `/sign-in` from protection check |
| `redirect` not working in action | Using `Response` instead of `throw redirect()` | Use `throw redirect('/path')` from `react-router` |
## Import Map
| What | Import From |
|------|-------------|
| `getAuth` | `@clerk/react-router/server` |
| `rootAuthLoader` | `@clerk/react-router/server` |
| `clerkMiddleware` | `@clerk/react-router/server` |
| `ClerkProvider` | `@clerk/react-router` |
| `useAuth`, `useUser` | `@clerk/react-router` |
| `OrganizationSwitcher` | `@clerk/react-router` |
## See Also
- `clerk-setup` - Initial Clerk install
- `clerk-custom-ui` - Custom flows & appearance
- `clerk-orgs` - B2B organizations
## Docs
[React Router SDK](https://clerk.com/docs/react-router/getting-started/quickstart)
From clerk/skills at a02dbd2, shown as plain text under its license: MIT (permissive). The license text is in skills/clerk-react-router-patterns/SKILL.md.
View SKILL.md on GitHub at a02dbd2Files
- SKILL.md7.4 KB · 5c019c33aa5b
- evals/
- evals.json4.7 KB · 040ac5cf9fb2
- references/
- loaders-actions.md1.7 KB · fa8d7f8e816e
- protected-routes.md1.7 KB · 4a1accbc9ed9
- ssr-auth.md1.5 KB · 0768df39ae20
- templates/
- react-router-basic-auth/
- app/
- app.css0 B · e3b0c44298fc
- root.tsx2.5 KB · 9ababc7e9252
- routes.ts132 B · 7578687c42a6
- routes/
- home.tsx57 B · da0ac7a6d09d
- package.json427 B · 4f38807e8c7d
- react-router.config.ts105 B · e0dd3485f9e3
- vite.config.ts499 B · dc22ffff743b
12 files, 21 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at a02dbd2More 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
Related in Frontend and design
react-email
React Email lets agents build HTML email templates as React components and render them to HTML or plain text.
by ResendOfficial publisher
Claude Code, Codex, Cursor +4 · about 3.4K tokens · pinned 6 days ago
apollo-client
This skill guides building React applications with Apollo Client 4.x for managing GraphQL queries, mutations, and state.
by Apollo GraphQLOfficial publisher
Claude Code, Codex, Cursor +4 · about 1.7K tokens · pinned 6 days ago
stitch::code-to-design
This skill converts existing frontend code (React, Vue, Angular, etc.) into a Stitch Design by extracting HTML, analyzing design systems, and uploading both…
by GoogleOfficial publisher
Claude Code, Codex, Cursor +4 · about 760 tokens · pinned 6 days ago
stitch::extract-design-md
Extracts a comprehensive design system document (DESIGN.md) by analyzing frontend source code including component files, stylesheets, Tailwind configs, and…
by GoogleOfficial publisher
Claude Code, Codex, Cursor +4 · about 3.3K tokens · pinned 6 days ago
stitch::extract-static-html
This skill extracts a self-contained static HTML file from a running web application or React components by inlining all CSS and images as base64 data.
by GoogleOfficial publisher
Contains scripts
Claude Code, Codex, Cursor +3 · about 2.6K tokens · pinned 6 days ago
frontend-design
This skill builds production-grade frontend interfaces and web components with distinctive, intentional design aesthetics.
by SnykOfficial publisher
Claude Code, Codex, Cursor +4 · about 990 tokens · pinned 6 days ago