关于 clerk-react-router-patterns
面向 React Router v7/v8 项目的 Clerk 认证技能,适用于需要在中间件、loader、action 和客户端组件中处理认证的开发者。内容涵盖服务端认证数据传递及版本相关配置注意事项。
主要功能
- 01loader 与 action 中的认证
- 02中间件与根路由认证数据传递
- 03受保护路由和重定向
- 04客户端认证钩子
- 05React Router v7/v8 配置说明
适用场景
- 01在 loader 中按用户身份加载数据
- 02在 action 中验证身份后处理表单
- 03保护路由并重定向未登录用户
- 04在 SSR 应用中向客户端提供认证状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-react-router-patterns安装后在 Claude Code 中输入/clerk-react-router-patterns确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/loaders-actions.mdprotected-routes.mdssr-auth.mdtemplates/react-router-basic-auth/package.jsonreact-router.config.tsvite.config.tstemplates/react-router-basic-auth/app/app.cssroot.tsxroutes.tstemplates/react-router-basic-auth/app/routes/home.tsx详细介绍
React Router 模式
SDK:@clerk/react-router v3.5+。支持 React Router v7.9+ 和 v8。
你需要什么?
| 任务 | 参考 |
|---|---|
| 在 loader 和 action 中进行身份验证 | references/loaders-actions.md |
| 受保护的路由和重定向 | references/protected-routes.md |
| SSR 用户数据和会话 | references/ssr-auth.md |
React Router v7 与 v8
在搭建项目之前,请检查已安装的 react-router 主版本——配置有所不同:
| v7.9+ | v8+ | |
|---|---|---|
| 中间件 API | 选择启用:在 react-router.config.ts 中设置 future: { v8_middleware: true } | 始终启用——切勿设置该标志(v8 已移除) |
ssr.noExternal 变通方案(如下) | 不需要 | 必须 |
最小设置
1. vite.config.ts(仅 v8——必需)
React Router v8 提供了开发/生产条件导出。在 react-router dev 中,
Vite 会在 SSR 时将 @clerk/react-router 外部化,因此 Node 会解析 react-router 的生产构建,
而应用代码使用开发构建——产生两个模块实例和两个 Router 上下文。之后,每个请求都会在 SSR 期间失败,并报错:
Error: useNavigate() may be used only in the context of a <Router> component.
**npm ls react-router 显示只有一个副本——这并不能排除该问题。**重复发生在每种导出条件下,而不是已安装的副本之间。不要排查重复安装问题;请添加变通方案(上游问题:
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>
)
}
@clerk/react-router 中没有 ClerkApp HOC(那是 @clerk/remix 的 API)。
在默认导出中渲染 <ClerkProvider loaderData={loaderData}>,并将根路由的 loaderData 传给它。
3. react-router.config.ts(仅 v7)
import type { Config } from '@react-router/dev/config'
export default {
future: {
v8_middleware: true,
},
} satisfies Config
在 v8 中,完全省略 future 块——该标志已不存在。
必需:必须在
root.tsx的 loader 中调用rootAuthLoader。否则,嵌套路由的 loader 中调用getAuth会抛出异常。
心智模型
React Router v7/v8 使用中间件 + loader 管线。Clerk 会接入这两个层级:
- 中间件(
clerkMiddleware())——在每个请求上运行,将身份验证信息附加到上下文 rootAuthLoader——必须在root.tsx中调用,以将 Clerk 状态传递给客户端getAuth(args)——在任何 loader/action 中调用,以获取当前用户
Request → clerkMiddleware() → rootAuthLoader → page loader → component
↓ ↓ ↓
attaches auth injects state getAuth(args)
to context to response reads context
在 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 }
}
在 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')
}
客户端组件
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>
}
组织切换
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) }
}
常见问题
| 现象 | 原因 | 修复方法 |
|---|---|---|
在 v8 开发环境中 SSR 时,ClerkProvider 抛出 useNavigate() may be used only in the context of a <Router> | Vite 开发环境的 SSR 会将 @clerk/react-router 外部化,随后它加载 react-router 的生产构建,而应用使用开发构建——导致两个 Router 上下文。npm ls 中只有一个副本并不能排除该问题。 | 在 vite.config.ts 中添加 ssr: { noExternal: ['@clerk/react-router'] }。切勿降级到 v7 |
构建错误:未导出 ClerkApp | @clerk/react-router 中不存在 ClerkApp | 在 root.tsx 的默认导出中使用 <ClerkProvider loaderData={loaderData}> |
clerkMiddleware() not detected | 缺少中间件(或在 v7 中缺少 v8_middleware future 标志) | 从根路由导出 middleware = [clerkMiddleware()];在 v7 中还要设置 future: { v8_middleware: true } |
| 未知的 future 标志错误/警告(v8) | 升级后,react-router.config.ts 中仍保留 v8_middleware 标志 | 删除 future.v8_middleware 条目——v8 中的中间件始终启用 |
getAuth 返回空的 userId | 未调用 rootAuthLoader | 在 root.tsx 的 loader 中调用 rootAuthLoader(args) |
| 无限重定向循环 | 重定向目标也受保护 | 在保护检查中排除 /sign-in |
action 中的 redirect 不起作用 | 使用了 Response 而不是 throw redirect() | 使用来自 react-router 的 throw redirect('/path') |
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 installclerk-custom-ui- Custom flows & appearanceclerk-orgs- B2B organizations