> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Login to Your React Router Application

> This guide demonstrates how to integrate Auth0 with a React Router application (framework mode, v7 or later) using the Auth0 React Router SDK.

> [!WARNING]
>
> `@auth0/auth0-react-router` is currently in **beta** (`1.0.0-beta.2`). The API may change before the stable 1.0 release.

> **Use AI to integrate Auth0**
>
> If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using [agent skills](https://agentskills.io/home).
>
> **Install:**
>
> ```bash theme={null}
> npx skills add auth0/agent-skills --skill auth0
> ```
>
> **Then ask your AI assistant:**
>
> ```text theme={null}
> Add Auth0 authentication to my React Router app
> ```
>
> Your AI assistant will automatically create your Auth0 application, fetch credentials, install `@auth0/auth0-react-router`, configure the provider, and set up your routes. [Full agent skills documentation →](https://auth0.com/docs/quickstart/agent-skills.md)

**Prerequisites:** Before you begin, ensure you have the following installed:

* **[Node.js](https://nodejs.org/en/download)** 18 or newer (20 LTS recommended)
* **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 9+, **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+, or **[pnpm](https://pnpm.io/installation)** 8+
* **[jq](https://jqlang.org/)** - Required for Auth0 CLI setup
* React Router framework mode, v7 or later (`react-router.config.ts` present)

## Get Started

This quickstart demonstrates how to add Auth0 authentication to a React Router application. You'll build a secure app with login, logout, and user profile features using the Auth0 React Router SDK. The SDK handles the OIDC flow server-side and stores the session in a JWE-encrypted cookie — tokens never reach the browser.

**1. Create a new React Router project**

Create a new React Router project for this Quickstart:

```shellscript theme={null}
npx create-react-router@latest my-app
```

Open the project:

```shellscript theme={null}
cd my-app && npm install
```

> [!NOTE]
>
> Skip this step if you are adding Auth0 to an existing React Router app.

**2. Install the Auth0 React Router SDK**

```shellscript theme={null}
npm install @auth0/auth0-react-router
```

**3. Configure Auth0**

Create an Auth0 application and set the callback and logout URLs.

**Quick Setup**

**CLI**

Run the following command from your project's root directory to create an Auth0 app and generate a `.env` file:

```shellscript Mac theme={null}
# Install Auth0 CLI (if not already installed)
brew tap auth0/auth0-cli && brew install auth0

# Set up Auth0 app and generate .env file
auth0 qs setup --app --type regular --framework react-router --port 5173 --name "My React Router App"
```

```powershell Windows theme={null}
# Install Auth0 CLI (if not already installed)
scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
scoop install auth0

# Set up Auth0 app and generate .env file
auth0 qs setup --app --type regular --framework react-router --port 5173 --name "My React Router App"
```

> [!NOTE]
>
> This command will create an Auth0 Regular Web Application configured for `http://localhost:5173` and generate a `.env` file with all required credentials. Skip step 4 if you use this option.

**Dashboard**

1. Go to [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > Applications**
2. Click **Create Application**, enter a name, and select **Regular Web Application**
3. Click **Create**
4. Open the **Settings** tab (Application Settings)
5. Set the following fields:

| Field | Value |
| - | - |
| Allowed Callback URLs | `http://localhost:5173/auth/callback` |
| Allowed Logout URLs | `http://localhost:5173` |

6. Click **Save Changes**
7. Copy **Domain** and **Client ID** from Application Settings — you will use them in the next step

**4. Configure environment variables**

Create a `.env` file at the root of your project:

| Variable | Where to find it |
| - | - |
| `AUTH0_DOMAIN` | Application Settings → Domain |
| `AUTH0_CLIENT_ID` | Application Settings → Client ID |
| `AUTH0_CLIENT_SECRET` | Application Settings → Client Secret |
| `AUTH0_SESSION_SECRET` | A random 32+ character string — auto-generated in the snippet above |
| `AUTH0_APP_BASE_URL` | Your app's base URL — `http://localhost:5173` for local development |

> [!WARNING]
>
> Never commit `.env` to version control. Add it to `.gitignore` before your first commit.

**5. Create the Auth0 server instance**

Create `app/auth0.server.ts`. The `.server.ts` suffix tells React Router's bundler to exclude this file from the client bundle, keeping your secrets server-only.

```ts app/auth0.server.ts theme={null}
import { Auth0Server, registerAuth0Instance } from '@auth0/auth0-react-router/server';

export const auth0 = new Auth0Server();
registerAuth0Instance(auth0);
```

**6. Add the auth routes**

Create a splat route that handles all `/auth/*` paths. `handleAuth` dispatches internally to `handleLogin`, `handleCallback`, `handleLogout`, and `handleBackchannelLogout` based on the URL path and HTTP method.

```tsx app/routes/auth.$.tsx theme={null}
import { handleAuth } from '@auth0/auth0-react-router/server';
import { auth0 } from '../auth0.server';

export const loader = ({ request }: { request: Request }) =>
  handleAuth(auth0, request);

export const action = ({ request }: { request: Request }) =>
  handleAuth(auth0, request);
```

Register the route in your route config:

```ts app/routes.ts theme={null}
import { type RouteConfig, route } from '@react-router/dev/routes';

export default [
  route('auth/*', 'routes/auth.$.tsx'),
  // ... your other routes
] satisfies RouteConfig;
```

**7. Configure the root layout**

Add `Auth0Provider` and `rootAuthLoader` to `app/root.tsx`. `rootAuthLoader` decrypts the session cookie and passes the auth state to the provider — no tokens are sent to the browser.

```tsx app/root.tsx expandable theme={null}
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
import { Auth0Provider } from '@auth0/auth0-react-router';
import { rootAuthLoader } from '@auth0/auth0-react-router/server';
import type { Route } from './+types/root';

export const loader = ({ request }: Route.LoaderArgs) =>
  rootAuthLoader(request);

export default function Root() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <Auth0Provider>
          <Outlet />
        </Auth0Provider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}
```

> [!NOTE]
>
> `Auth0Provider` reads session data from `useRouteLoaderData('root')`, so the root route must have the id `root`. With file-based routing React Router sets this from the filename automatically. With a custom route config, pass `{ id: 'root' }` to the `layout()` call.

**8. Add login and logout**

Use the built-in components to show login and logout controls. `LoginButton` redirects to `/auth/login` and `LogoutButton` redirects to `/auth/logout`. Auth0 handles the OIDC flow and redirects the user back to your app after sign-in.

```tsx app/routes/_index.tsx theme={null}
import {
  AuthLoading,
  LoginButton,
  LogoutButton,
  SignedIn,
  SignedOut,
} from '@auth0/auth0-react-router';

export default function Home() {
  return (
    <main>
      <h1>Welcome</h1>
      <AuthLoading>
        <p>Loading…</p>
      </AuthLoading>
      <SignedOut>
        <LoginButton>Log in</LoginButton>
      </SignedOut>
      <SignedIn>
        <LogoutButton>Log out</LogoutButton>
      </SignedIn>
    </main>
  );
}
```

**9. Show the user profile**

Use the `useUser` hook to access the authenticated user's profile in any client component. Pair it with `requireSession` in the loader to block unauthenticated requests at the server before the page renders.

```tsx app/routes/profile.tsx theme={null}
import { useUser } from '@auth0/auth0-react-router';
import { requireSession } from '@auth0/auth0-react-router/server';
import type { Route } from './+types/profile';

export const loader = async ({ request }: Route.LoaderArgs) => {
  await requireSession(request);
  return null;
};

export default function Profile() {
  const user = useUser();

  return (
    <main>
      <h1>Profile</h1>
      {user && (
        <div>
          <img src={user.picture} alt={user.name ?? ''} width={80} height={80} />
          <p><strong>{user.name}</strong></p>
          <p>{user.email}</p>
        </div>
      )}
    </main>
  );
}
```

**10. Run your application**

```shellscript theme={null}
npm run dev
```

Open `http://localhost:5173` in your browser and click **Log in**. You will be redirected to the Auth0 Universal Login page. After signing in you will be redirected back to your app.

> [!TIP]
>
> Your app now has working login and logout. The session is stored in a JWE-encrypted cookie — access tokens stay on the server and are never sent to the browser.

## Troubleshooting

> **JWEDecryptionFailed — session cookie cannot be decrypted**
>
> **Cause:** `AUTH0_SESSION_SECRET` changed after a session cookie was issued, or the value is fewer than 32 characters.
>
> **Fix:** Clear your browser cookies for `localhost`, confirm `AUTH0_SESSION_SECRET` is at least 32 characters, and restart the dev server. To generate a new secret: `openssl rand -hex 32`.

> **Callback URL mismatch — Auth0 returns an error after login**
>
> **Cause:** The redirect URL Auth0 receives does not match any value in Allowed Callback URLs.
>
> **Fix:** In [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > Applications** → select your app → **Application Settings**, confirm **Allowed Callback URLs** is set to `http://localhost:5173/auth/callback`. Remove any trailing slashes or extra whitespace, then click **Save Changes**.

> **404 on /auth/login — route not found**
>
> **Cause:** The `auth.$.tsx` splat route is missing or not registered in `routes.ts`.
>
> **Fix:** Confirm `app/routes/auth.$.tsx` exists and that `app/routes.ts` includes `route('auth/*', 'routes/auth.$.tsx')`. Restart the dev server after editing `routes.ts`.

> **useUser returns null after login**
>
> **Cause:** `rootAuthLoader` is not exported from `app/root.tsx`, or the root route does not have the id `root`.
>
> **Fix:** Confirm `app/root.tsx` exports `export const loader = ({ request }) => rootAuthLoader(request)`. With a custom route config, register the root layout as `layout('root.tsx', { id: 'root' }, [...routes])`.

> **TypeError on context.get — middleware not working**
>
> **Cause:** `defineRouteAuth` and `auth0Middleware` require React Router 7.9.0 or later, which introduced the middleware API.
>
> **Fix:** Upgrade `react-router` to `>=7.9.0`, or protect routes individually using `requireSession` / `requireUser` in each loader instead.

## Advanced Usage

> **Call a backend API with an access token**
>
> Add `AUTH0_AUDIENCE` to `.env` with your API's identifier (from [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > APIs** → **API Settings → Identifier**). Then use `getAccessToken` in a loader — the token never reaches the browser:
>
> ```ts app/routes/data.tsx theme={null}
> import { getAccessToken } from '@auth0/auth0-react-router/server';
> import { TokenError } from '@auth0/auth0-react-router/errors';
>
> export const loader = async ({ request }) => {
>   let token: string;
>   try {
>     token = await getAccessToken(request);
>   } catch (err) {
>     if (err instanceof TokenError) {
>       return new Response(null, { status: 302, headers: { Location: '/auth/login' } });
>     }
>     throw err;
>   }
>   const data = await fetch('https://api.example.com/items', {
>     headers: { Authorization: `Bearer ${token}` },
>   }).then(r => r.json());
>   return { data };
> };
> ```

> **Role-based route protection**
>
> Use `defineRouteAuth` middleware (React Router ≥ 7.9.0) to enforce roles at the route level. Roles are read from the `https://auth0.com/claims/roles` claim by default:
>
> ```ts app/routes/admin.tsx theme={null}
> import { defineRouteHandle } from '@auth0/auth0-react-router';
> import { defineRouteAuth, auth0UserContext } from '@auth0/auth0-react-router/server';
>
> export const handle = defineRouteHandle({ role: 'admin' });
>
> export const middleware = defineRouteAuth({ role: 'admin' }).middleware;
>
> export const loader = ({ context }) => {
>   const user = context.get(auth0UserContext);
>   return { user };
> };
> ```
>
> Requests without the required role receive a `403`.

> **SPA mode (client-side PKCE)**
>
> The SDK can run in a purely client-side mode backed by `@auth0/auth0-spa-js`. Add `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_CLIENT_ID` to your `.env` — `Auth0Provider` detects these automatically and switches to the PKCE flow. No other code changes are required.
>
> ```shellscript theme={null}
> # .env additions for SPA mode
> VITE_AUTH0_DOMAIN={yourDomain}
> VITE_AUTH0_CLIENT_ID={yourClientId}
> ```
>
> > [!WARNING]
> >
> > Do not set both `AUTH0_*` and `VITE_AUTH0_*` variables at the same time. Hybrid mode is not supported — when both are present, SPA logout will not clear the server-side session cookie.
