clerk-react-router-patterns

React Router v7 patterns with Clerk — rootAuthLoader, getAuth in loaders,

INSTALLATION
npx skills add https://github.com/clerk/skills --skill clerk-react-router-patterns
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

React Router Patterns

SDK: @clerk/react-router v3+. Requires React Router v7.9+.

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

Mental Model

React Router v7 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

Minimal Setup

1. root.tsx

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 />

})

2. Middleware (root route or entry.server.ts)

import { clerkMiddleware } from '@clerk/react-router/server'

export const middleware = [clerkMiddleware()]

Required: rootAuthLoader must be called in root.tsx's loader. Without it, getAuth throws in nested loaders.

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

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

Import Map

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

See Also

  • clerk-setup - Initial Clerk install
  • clerk-custom-ui - Custom flows &#x26; appearance
  • clerk-orgs - B2B organizations

Docs

React Router SDK

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card