āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/clerk/clerk-docs/getting-started/quickstart.react-router ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
<TutorialHero exampleRepo={[ { title: "React Router Quickstart Repo", link: "https://github.com/clerk/clerk-react-router-quickstart"
}
]} beforeYouStart={[ { title: "Set up a Clerk application", link: "/docs/getting-started/quickstart/setup-clerk", icon: "clerk", }, { title: "Create a React Router application", link: "https://reactrouter.com/start/framework/installation", icon: "react-router", } ]} />
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.1.2 or later in framework mode.
<Steps> ## Install `@clerk/react-router`The Clerk React Router SDK provides prebuilt components, hooks, and helpers to make it easy to integrate authentication and user management in your React Router app.
Run the following command to install the SDK:
<CodeBlockTabs options={["npm", "yarn", "pnpm", "bun"]}>
bash {{ filename: 'terminal' }} npm install @clerk/react-router
```bash {{ filename: 'terminal' }}
yarn add @clerk/react-router
```
```bash {{ filename: 'terminal' }}
pnpm add @clerk/react-router
```
```bash {{ filename: 'terminal' }}
bun add @clerk/react-router
```
</CodeBlockTabs>
The final result should resemble the following:
</SignedOut>
VITE_CLERK_PUBLISHABLE_KEY={{pub_key}}
CLERK_SECRET_KEY={{secret}}
clerkMiddleware() and rootAuthLoader() to your appclerkMiddleware() grants you access to user authentication state throughout your app.
React Router middleware requires opting in via a future flag. Add the following to your react-router.config.ts file:
import type { Config } from '@react-router/dev/config'
export default {
// ...
future: {
v8_middleware: true,
},
} satisfies Config
Then, add the following code to your root.tsx file to configure the clerkMiddleware() and rootAuthLoader() functions.
To load additional data or configure options, see the clerkMiddleware() reference.
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import type { Route } from './+types/root'
import stylesheet from './app.css?url'
import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'
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>
)
}
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>
)
}
<ClerkProvider> and Clerk components to your appIt's required to pass loaderData to the <ClerkProvider> component. This data is provided by the rootAuthLoader() function.
The following example adds <ClerkProvider> and creates a header using the following Clerk components:
<SignedIn>: Children of this component can only be seen while signed in.<SignedOut>: 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.import { ClerkProvider, SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/react-router'
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'
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 the sign-in button when the user is signed out */}
<SignedOut>
<SignInButton />
</SignedOut>
{/* Show the user button when the user is signed in */}
<SignedIn>
<UserButton />
</SignedIn>
</header>
<Outlet />
</ClerkProvider>
)
}
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>
)
}
Run your project with the following command:
<CodeBlockTabs options={["npm", "yarn", "pnpm", "bun"]}>
bash {{ filename: 'terminal' }} npm run dev
```bash {{ filename: 'terminal' }}
yarn dev
```
```bash {{ filename: 'terminal' }}
pnpm dev
```
```bash {{ filename: 'terminal' }}
bun dev
```
</CodeBlockTabs>
Visit your app's homepage at http://localhost:5173. Sign up to create your first user.
</Steps>
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā