React Router Quickstart
Hand this prompt to your agent to add auth to your app with the Clerk CLI. No need to leave your terminal or copy and paste API keys.
# Add Clerk Authentication
Set up Clerk authentication with the Clerk CLI. When the framework supports keyless, `npx clerk@latest init` defaults to keyless mode — auto-generated temporary development keys that a later `clerk auth login` claims automatically.
## Before you start
Show the user this checklist and wait for a yes:
```
Here's what I'll do to get you set up with Clerk.
1. Set up Clerk in this project, or scaffold a new app if this directory is empty
2. Start your app with Clerk installed.
Shall I proceed?
```
## Step 1a: Existing project
From the project root:
```bash
npx clerk@latest init
```
`init` detects the framework and package manager, installs the SDK, and applies framework setup — provider, middleware, auth routes, env. Do not pass `--framework` or `--pm` unless the user wants to override detection. Do not list apps or ask which Clerk app to use.
## Step 1b: Empty directory
Ask which framework and package manager to use, defaulting to Next.js and npm:
```bash
npx clerk@latest init --framework <framework> --pm <package-manager>
```
If a lockfile is present, let it pick the package manager: `pnpm-lock.yaml` -> `pnpm`, `yarn.lock` -> `yarn`, `bun.lock` or `bun.lockb` -> `bun`, `package-lock.json` -> `npm`.
## Step 1c: Development keys
Next.js, Astro, Nuxt, TanStack Start, and React Router support keyless. There `init` writes temporary development keys to the project's env file, so the user needs no Clerk account. The CLI prints a confirmation naming the env file it wrote, followed by:
```
When you're ready, run clerk auth login and your app will be claimed automatically.
```
Relay that, using the filename the CLI printed: the app stays unclaimed until the user runs `npx clerk@latest auth login`. Do not run it for them unless they ask to claim now.
Every other framework needs real API keys. There `init` applies what setup it can and prints the remaining steps.
To link an existing Clerk application, add `--app <application_id>` — but only when the user supplies the ID. If they want to link and have no ID, run `npx clerk@latest apps list --json`, show the names and IDs, and ask. Never choose an application for them.
## Step 2: Fall back to docs when init is incomplete
If `init` reports the framework is unsupported or undetected, follow the quickstart instead.
`init` scaffolds Next.js (App and Pages Router), React, React Router, Nuxt, TanStack Start, Astro, Vue, JavaScript/Vite, Expo, Express, Fastify, iOS, and Android.
| Framework | Quickstart |
| ----------------------- | ---------------------------------------------------------------------- |
| `next` | https://clerk.com/docs/nextjs/getting-started/quickstart |
| `astro` | https://clerk.com/docs/astro/getting-started/quickstart |
| `nuxt` | https://clerk.com/docs/nuxt/getting-started/quickstart |
| `react-router` | https://clerk.com/docs/react-router/getting-started/quickstart |
| `@tanstack/react-start` | https://clerk.com/docs/tanstack-react-start/getting-started/quickstart |
| `react` | https://clerk.com/docs/react/getting-started/quickstart |
| `vue` | https://clerk.com/docs/vue/getting-started/quickstart |
| `vite` or vanilla JS | https://clerk.com/docs/js-frontend/getting-started/quickstart |
| `express` | https://clerk.com/docs/expressjs/getting-started/quickstart |
| `fastify` | https://clerk.com/docs/fastify/getting-started/quickstart |
| `expo` | https://clerk.com/docs/expo/getting-started/quickstart |
| iOS (Swift) | https://clerk.com/docs/ios/getting-started/quickstart |
| Android (Kotlin) | https://clerk.com/docs/android/getting-started/quickstart |
| Chrome Extension | https://clerk.com/docs/chrome-extension/getting-started/quickstart |
Everything else: https://clerk.com/docs/llms.txt
## Step 3: Add visible auth controls
The app needs sign-in, sign-up, and signed-in user controls, worked into the existing layout or navigation. If they already exist, adapt them instead of duplicating.
For Next.js App Router:
```text
import { SignInButton, SignUpButton, Show, UserButton } from '@clerk/nextjs'
<>
<Show when="signed-out">
<SignInButton />
<SignUpButton />
</Show>
<Show when="signed-in">
<UserButton />
</Show>
</>
```
Other frameworks use the same component names from their own Clerk package — `@clerk/vue`, `@clerk/react`, `@clerk/nuxt`, and so on.
## Step 4: Verify
```bash
npx clerk@latest doctor
```
Then start the app, confirm the auth controls render, and fix anything the CLI reports.
## Step 5: If using shadcn/ui
If `components.json` exists in the project root, add `@clerk/ui` with the package manager from Step 1 — `npm install`, `pnpm add`, `yarn add`, or `bun add`.
Apply the theme in your provider:
```text
import { shadcn } from '@clerk/ui/themes'
<ClerkProvider appearance={{ theme: shadcn }}>{children}</ClerkProvider>
```
Add to global CSS:
```css
@import '@clerk/ui/themes/shadcn.css';
```
## Critical rules
- Next.js 15+: `auth()` is async. Always `await auth()`
- `ClerkProvider` goes inside `<body>`, not wrapping `<html>`
- Never expose `CLERK_SECRET_KEY` in client code
- Use `@clerk/nextjs`, not `@clerk/clerk-react`
- Do not read or print existing environment variable files; ask the user for any missing non-sensitive configuration
Docs: https://clerk.com/docs/cli https://clerk.com/docs/llms.txt
## After Setup
Have the user sign up as their first test user. Congratulate them once the profile icon appears in the nav.
Then offer Organizations — multi-tenancy, team invitations, roles and permissions, and enterprise SSO.
If yes:
1. Run `npx clerk@latest enable orgs`.
2. Add `<OrganizationSwitcher />` next to the existing `<UserButton />`, or the framework equivalent.
3. Have them create an organization from the switcher and invite a teammate.
If no, point them to Organizations (https://clerk.com/docs/guides/organizations/overview), Components (https://clerk.com/docs/reference/components/overview), and the Dashboard (https://dashboard.clerk.com/).Or set up Clerk yourself by following the step-by-step instructions.
React Router can be used in different modes: declarative, data, or framework. This tutorial explains how to use React Router in framework mode. To use React Router in declarative mode instead, see the dedicated guide.
This tutorial assumes that you're using React Router v7.9.0 or later, or v8.3.0 or later in framework mode.
Step-by-step setup instructions
Create a new React app using React Router
If you don't already have a React app using React Router, run the following commands to create a new one.
npm create react-router@latest clerk-react-router
cd clerk-react-routerpnpm create react-router clerk-react-router
cd clerk-react-routeryarn create react-router clerk-react-router
cd clerk-react-routerbunx create-react-router clerk-react-router
cd clerk-react-routerInstall @clerk/react-router
The Clerk React Router SDK gives you access to prebuilt components, hooks, and helpers to make user authentication easier.
Run the following command to install the SDK:
npm install @clerk/react-routerpnpm add @clerk/react-routeryarn add @clerk/react-routerbun add @clerk/react-routerSet your Clerk API keys
Add the following keys to your .env file. These keys can always be retrieved from the API keys page in the Clerk Dashboard.
VITE_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEYAdd clerkMiddleware() and rootAuthLoader() to your app
clerkMiddleware() grants you access to user authentication state throughout your app. It also allows you to protect specific routes from unauthenticated users. To add clerkMiddleware() to your app, follow these steps:
-
Add the following code to your
root.tsxfile to configure theclerkMiddleware()and rootAuthLoader() functions.app /root.tsx import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server' export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args)3 lines collapsed
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' import type { Route } from './+types/root' import stylesheet from './app.css?url'62 lines collapsed
export const links: Route.LinksFunction = () => [ { rel: 'preconnect', href: 'https://fonts.googleapis.com' }, { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossOrigin: 'anonymous', }, { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', }, { rel: 'stylesheet', href: stylesheet }, ] export function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <Meta /> <Links /> </head> <body> {children} <ScrollRestoration /> <Scripts /> </body> </html> ) } export default function App() { return <Outlet /> } export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { let message = 'Oops!' let details = 'An unexpected error occurred.' let stack: string | undefined if (isRouteErrorResponse(error)) { message = error.status === 404 ? '404' : 'Error' details = error.status === 404 ? 'The requested page could not be found.' : error.statusText || details } else if (import.meta.env.DEV && error && error instanceof Error) { details = error.message stack = error.stack } return ( <main className="pt-16 p-4 container mx-auto"> <h1>{message}</h1> <p>{details}</p> {stack && ( <pre className="w-full p-4 overflow-x-auto"> <code>{stack}</code> </pre> )} </main> ) } -
By default,
clerkMiddleware()will not protect any routes. All routes are public and you must opt-in to protection for routes. See the clerkMiddleware() reference to learn how to require authentication for specific routes.
Add <ClerkProvider> and Clerk components to your app
The <ClerkProvider> component provides session and user context to Clerk's hooks and components. It's recommended to wrap your entire app at the entry point with <ClerkProvider> to make authentication globally accessible. See the reference docs for other configuration options.
Copy and paste the following code into your root.tsx file. This:
- Adds the
<ClerkProvider>to your app, providing Clerk's authentication context to your app. PassloaderDatato the provider so it can access the authentication data loaded byrootAuthLoader(). - Creates a header with Clerk's prebuilt components to allow users to sign in and out, and display different content for signed-in and signed-out users.
import { ClerkProvider, SignInButton, SignUpButton, Show, UserButton } from '@clerk/react-router'
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'
39 lines collapsed
import type { Route } from './+types/root'
import stylesheet from './app.css?url'
export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]
export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args)
export const links: Route.LinksFunction = () => [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{
rel: 'preconnect',
href: 'https://fonts.gstatic.com',
crossOrigin: 'anonymous',
},
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
{ rel: 'stylesheet', href: stylesheet },
]
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
)
}
// Pull in the `loaderData` from the `rootAuthLoader()` function
export default function App({ loaderData }: Route.ComponentProps) {
return (
// Pass the `loaderData` to the `<ClerkProvider>` component
<ClerkProvider loaderData={loaderData}>
<header className="flex items-center justify-center py-8 px-4">
<Show when="signed-out">
<SignInButton />
<SignUpButton />
</Show>
<Show when="signed-in">
<UserButton />
</Show>
</header>
<Outlet />
</ClerkProvider>
)
}
26 lines collapsed
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = 'Oops!'
let details = 'An unexpected error occurred.'
let stack: string | undefined
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? '404' : 'Error'
details =
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message
stack = error.stack
}
return (
<main className="pt-16 p-4 container mx-auto">
<h1>{message}</h1>
<p>{details}</p>
{stack && (
<pre className="w-full p-4 overflow-x-auto">
<code>{stack}</code>
</pre>
)}
</main>
)
}This example uses the following components:
- <Show when="signed-in">: Children of this component can only be seen while signed in.
- <Show when="signed-out">: Children of this component can only be seen while signed out.
- <UserButton />: Shows the signed-in user's avatar. Selecting it opens a dropdown menu with account management options.
- <SignInButton />: An unstyled component that links to the sign-in page. In this example, since no props or environment variables are set for the sign-in URL, this component links to the Account Portal sign-in page.
- <SignUpButton />: An unstyled component that links to the sign-up page. In this example, since no props or environment variables are set for the sign-up URL, this component links to the Account Portal sign-up page.
Run your project
Run your project with the following command:
npm run devpnpm run devyarn devbun run devCreate your first user
- Visit your app's homepage at http://localhost:5173.
- Select "Sign up" on the page and authenticate to create your first user.
Next steps
Explore the most relevant next steps for your SDK using the following guides.
Prebuilt components
Learn how to add Clerk's prebuilt authentication and user-management UI to your app.
Build custom flows
Learn how to build custom user interfaces entirely from scratch using the Clerk API.
Read user data
Learn how to use Clerk's helpers to read user data in your app.
Customization & localization
Learn how to customize and localize Clerk components.
More to explore
Explore additional Clerk features that help you build, manage, and grow your application.
- Organizations - Organizations are shared accounts that let teams collaborate, manage members and roles, and control access to shared resources.
- Billing - Billing enables you to manage subscriptions, free trials, payments, plans, and billing-related webhook events for B2C and B2B applications.
- Waitlist - Waitlist lets you collect signups and control access to new products or features before launch through a simple, integrated workflow.
Feedback
Last updated on