clerk-react-router-patterns

bởi clerk

Các mẫu React Router v7 với Clerk — rootAuthLoader, getAuth trong loaders,

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

React Router Patterns

SDK: @clerk/react-router v3.5+. Supports React Router v7.9+ and v8.

What Do You Need?

TaskReference
Auth in loaders and actionsreferences/loaders-actions.md
Protected routes and redirectsreferences/protected-routes.md
SSR user data and sessionreferences/ssr-auth.md

React Router v7 vs v8

Check the installed react-router major version before scaffolding — the config differs:

v7.9+v8+
Middleware APIOpt-in: set future: { v8_middleware: true } in react-router.config.tsAlways on — do NOT set the flag (v8 removed it)
ssr.noExternal workaround (below)Not neededRequired

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

import { reactRouter } from '@react-router/dev/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [reactRouter()],
  ssr: {
    noExternal: ['@clerk/react-router'],
  },
})

2. root.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)

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

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

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

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

import { OrganizationSwitcher } from '@clerk/react-router'

export function Nav() {
  return <OrganizationSwitcher afterSelectOrganizationUrl="/dashboard" />
}
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

SymptomCauseFix
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 exportedClerkApp does not exist in @clerk/react-routerUse <ClerkProvider loaderData={loaderData}> in root.tsx's default export
clerkMiddleware() not detectedMissing 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 upgradingRemove the future.v8_middleware entry — middleware is always on in v8
getAuth returns empty userIdrootAuthLoader not calledCall rootAuthLoader(args) in root.tsx loader
Infinite redirect loopRedirect target is also protectedExclude /sign-in from protection check
redirect not working in actionUsing Response instead of throw redirect()Use throw redirect('/path') from react-router

Import Map

WhatImport 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

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-nextjs-patterns
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...
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,