From features
Provides React Router v7 patterns with Clerk for auth: rootAuthLoader, getAuth in loaders/actions, clerkMiddleware, protected routes, SSR user/org data.
npx claudepluginhub clerk/skills --plugin mobileThis skill is limited to using the following tools:
SDK: `@clerk/react-router` v3+. Requires React Router v7.9+.
evals/evals.jsonreferences/loaders-actions.mdreferences/protected-routes.mdreferences/ssr-auth.mdtemplates/react-router-basic-auth/app/app.csstemplates/react-router-basic-auth/app/root.tsxtemplates/react-router-basic-auth/app/routes.tstemplates/react-router-basic-auth/app/routes/home.tsxtemplates/react-router-basic-auth/package.jsontemplates/react-router-basic-auth/react-router.config.tstemplates/react-router-basic-auth/vite.config.tsProvides React SPA authentication patterns using @clerk/react for Vite/CRA, including ClerkProvider setup, useAuth/useUser/useClerk hooks, React Router protected routes, and custom sign-in flows.
Implements Clerk middleware for session management and route protection in Next.js apps. Configures auth() patterns, custom JWT templates, and organization-scoped sessions.
Provides expert Clerk auth patterns for Next.js App Router: ClerkProvider setup, SignIn/SignUp components, middleware route protection, and anti-patterns.
Share bugs, ideas, or general feedback.
SDK: @clerk/react-router v3+. Requires React Router v7.9+.
| 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 uses a middleware + loader pipeline. Clerk plugs into both layers:
clerkMiddleware()) — runs on every request, attaches auth to contextrootAuthLoader — required in root.tsx to pass Clerk state to the clientgetAuth(args) — called inside any loader/action to get the current userRequest → clerkMiddleware() → rootAuthLoader → page loader → component
↓ ↓ ↓
attaches auth injects state getAuth(args)
to context to response reads context
import { rootAuthLoader } from '@clerk/react-router/server'
import { ClerkApp } from '@clerk/react-router'
import type { Route } from './+types/root'
export async function loader(args: Route.LoaderArgs) {
return rootAuthLoader(args)
}
export default ClerkApp(function App() {
return <Outlet />
})
import { clerkMiddleware } from '@clerk/react-router/server'
export const middleware = [clerkMiddleware()]
Required:
rootAuthLoadermust be called inroot.tsx's loader. Without it,getAuththrows in nested 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 }
}
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) }
}
| Symptom | Cause | Fix |
|---|---|---|
clerkMiddleware() not detected | Missing middleware | Export middleware = [clerkMiddleware()] from root route |
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 |
| What | Import From |
|---|---|
getAuth | @clerk/react-router/server |
rootAuthLoader | @clerk/react-router/server |
clerkMiddleware | @clerk/react-router/server |
ClerkApp | @clerk/react-router |
useAuth, useUser | @clerk/react-router |
OrganizationSwitcher | @clerk/react-router |
clerk-setup - Initial Clerk installclerk-custom-ui - Custom flows & appearanceclerk-orgs - B2B organizations