Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions docs/_partials/organization-sync-options.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
The `organizationSyncOptions` property on the [`clerkMiddleware()`](#clerk-middleware-options) options
object has the type `OrganizationSyncOptions`, which has the following properties:

<Properties>
- `organizationPatterns`
- <code>[Pattern](#pattern)\[]</code>

Specifies URL patterns that are organization-specific, containing an organization ID or slug as a path parameter. If a request
matches this path, the organization identifier will be used to set that org as active.

If the route also matches the `personalAccountPatterns` prop, this prop takes precedence.

Patterns must have a path parameter named either `:id` (to match a Clerk organization ID) or `:slug` (to match a Clerk organization slug).

> [!WARNING]
> If the organization can't be activated—either because it doesn't exist or the user lacks access—the previously [active organization](!active-organization) will remain unchanged. Components must detect this case and provide an appropriate error and/or resolution pathway, such as calling `notFound()` or displaying an [`<OrganizationSwitcher />`](/docs/reference/components/organization/organization-switcher).

Common examples:

- `["/orgs/:slug", "/orgs/:slug/(.*)"]`
- `["/orgs/:id", "/orgs/:id/(.*)"]`
- `["/app/:any/orgs/:slug", "/app/:any/orgs/:slug/(.*)"]`

---

- `personalAccountPatterns`
- <code>[Pattern](#pattern)\[]</code>

URL patterns for resources that exist within the context of a user's [personal account](/docs/guides/organizations/overview#allow-personal-accounts).

If the route also matches the `organizationPattern` prop, the `organizationPattern` prop takes precedence.

Common examples:

- `["/me", "/me/(.*)"]`
- `["/user/:any", "/user/:any/(.*)"]`
</Properties>

### Pattern

A `Pattern` is a `string` that represents the structure of a URL path. In addition to any valid URL, it may include:

- Named path parameters prefixed with a colon (e.g., `:id`, `:slug`, `:any`).
- Wildcard token, `(.*)`, which matches the remainder of the path.

#### Examples

- `/orgs/:slug`

| URL | Matches | `:slug` value |
| - | - | - |
| `/orgs/acmecorp` | ✅ | `acmecorp` |
| `/orgs` | ❌ | n/a |
| `/orgs/acmecorp/settings` | ❌ | n/a |

- `/app/:any/orgs/:id`

| URL | Matches | `:id` value |
| - | - | - |
| `/app/petstore/orgs/org_123` | ✅ | `org_123` |
| `/app/dogstore/v2/orgs/org_123` | ❌ | n/a |

- `/personal-account/(.*)`

| URL | Matches |
| - | - |
| `/personal-account/settings` | ✅ |
| `/personal-account` | ❌ |
7 changes: 2 additions & 5 deletions docs/getting-started/quickstart.js-backend.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,7 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan

```tsx {{ filename: 'app/routes/profile.tsx' }}
import { redirect } from 'react-router'
import { getAuth } from '@clerk/react-router/ssr.server'
import { createClerkClient } from '@clerk/react-router/api.server'
import { clerkClient, getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'

export async function loader(args: Route.LoaderArgs) {
Expand All @@ -433,9 +432,7 @@ The [`Auth`](/docs/reference/backend/types/auth-object) object contains importan
}

// Use the JS Backend SDK's `getUser()` method to get the Backend User object
const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser(
userId,
)
const user = await clerkClient(args).users.getUser(userId)

// Return the Backend User object
return {
Expand Down
107 changes: 94 additions & 13 deletions docs/getting-started/quickstart.react-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,36 @@ This tutorial assumes that you're using React Router **v7.1.2 or later** in fram
CLERK_SECRET_KEY={{secret}}
```

## Configure `rootAuthLoader()`
## Add `clerkMiddleware()` and `rootAuthLoader()` to your app

The `rootAuthLoader()` function provides access to authentication state in any React Router route.
`clerkMiddleware()` grants you access to user authentication state throughout your app.

The following code shows how to add this function to your `root.tsx` file. If you're using [Clerk's React Router quickstart](https://github.com/clerk/clerk-react-router-quickstart) or the [React Router template](https://reactrouter.com/start/framework/installation), most of this code will already be present.
React Router middleware requires opting in via a future flag. Add the following to your `react-router.config.ts` file:

To load additional data or configure options, see the [`rootAuthLoader()`](/docs/reference/react-router/root-auth-loader) reference.
```ts {{ filename: 'react-router.config.ts' }}
import type { Config } from '@react-router/dev/config'

```tsx {{ filename: 'app/root.tsx', mark: [1, [6, 8]], collapsible: true }}
import { rootAuthLoader } from '@clerk/react-router/ssr.server'
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()`](/docs/reference/react-router/clerk-middleware) reference.

```tsx {{ filename: 'app/root.tsx', mark: [4, [6, 8]], fold: [[1, 3], [10, 71]], collapsible: true }}
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 async function loader(args: Route.LoaderArgs) {
return rootAuthLoader(args)
}
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' },
Expand Down Expand Up @@ -169,18 +182,61 @@ This tutorial assumes that you're using React Router **v7.1.2 or later** in fram
- [`<UserButton />`](/docs/reference/components/user/user-button): Shows the signed-in user's avatar. Selecting it opens a dropdown menu with account management options.
- [`<SignInButton />`](/docs/reference/components/unstyled/sign-in-button): An unstyled component that links to the sign-in page. In this example, since no props or [environment variables](/docs/guides/development/clerk-environment-variables) are set for the sign-in URL, this component links to the [Account Portal sign-in page](/docs/guides/customizing-clerk/account-portal#sign-in).

```tsx {{ filename: 'app/root.tsx' }}
// Other imports

```tsx {{ filename: 'app/root.tsx', mark: [44, [46, 58]], fold: [[2, 42], [62, 87]] }}
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>
Expand All @@ -192,7 +248,32 @@ This tutorial assumes that you're using React Router **v7.1.2 or later** in fram
)
}

// Rest of the root.tsx code
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>
)
}
```

## Create your first user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ In the following example, the `acceptsToken` parameter is set to only accept `oa
- If the token is valid, it returns the authenticated user's subject and their associated scopes for use in the application logic.

```tsx
import { getAuth } from '@clerk/react-router/ssr.server'
import { getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'

export async function loader(args: Route.LoaderArgs) {
Expand All @@ -43,7 +43,7 @@ In the following example, the `acceptsToken` parameter is set to accept any toke
- Otherwise, it logs that the request uses a machine token and specifies its type.

```tsx
import { getAuth } from '@clerk/react-router/ssr.server'
import { getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'

export async function loader(args: Route.LoaderArgs) {
Expand Down
5 changes: 2 additions & 3 deletions docs/guides/sessions/customize-session-tokens.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,12 @@ This guide will show you how to customize a session token to include additional

```tsx {{ filename: 'app/routes/profile.tsx' }}
import { redirect } from 'react-router'
import { getAuth } from '@clerk/react-router/ssr.server'
import { createClerkClient } from '@clerk/react-router/api.server'
import { getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'

export async function loader(args: Route.LoaderArgs) {
// Use `getAuth()` to access `isAuthenticated` and the user's ID and session claims
const { isAuthenticated, userId, sessionClaims } = await getAuth(args)
const { isAuthenticated, sessionClaims } = await getAuth(args)

// Protect the route by checking if the user is signed in
if (!isAuthenticated) {
Expand Down
9 changes: 3 additions & 6 deletions docs/guides/sessions/session-tokens.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,7 @@ Then, when you need to access the other metadata fields, you can fetch them usin
<Tab>
```tsx {{ filename: 'app/routes/profile.tsx' }}
import { redirect } from 'react-router'
import { getAuth } from '@clerk/react-router/ssr.server'
import { createClerkClient } from '@clerk/react-router/api.server'
import { clerkClient, getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'

export async function loader(args: Route.LoaderArgs) {
Expand All @@ -269,10 +268,8 @@ Then, when you need to access the other metadata fields, you can fetch them usin
return redirect('/sign-in?redirect_url=' + args.request.url)
}

// Use the JS Backend SDK's `getUser()` method to get the Backend User object
const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser(
userId,
)
// Get the Backend User object
const user = await clerkClient(args).users.getUser(userId)

// Return the Backend User object
return {
Expand Down
18 changes: 6 additions & 12 deletions docs/guides/users/reading.react-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ In the following example, the `userId` is passed to the JS Backend SDK's `getUse

```tsx {{ filename: 'app/routes/profile.tsx' }}
import { redirect } from 'react-router'
import { getAuth } from '@clerk/react-router/ssr.server'
import { createClerkClient } from '@clerk/react-router/api.server'
import { clerkClient, getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile'

export async function loader(args: Route.LoaderArgs) {
Expand All @@ -33,10 +32,8 @@ export async function loader(args: Route.LoaderArgs) {
return redirect('/sign-in?redirect_url=' + args.request.url)
}

// Instantiate the JS Backend SDK and get the user's full `Backend User` object
const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser(
userId,
)
// Get the user's full `Backend User` object
const user = await clerkClient(args).users.getUser(userId)

return {
user: JSON.stringify(user),
Expand All @@ -61,8 +58,7 @@ Unlike the previous example that loads data when the page loads, the following e

```tsx {{ filename: 'app/routes/profile-form.tsx' }}
import { redirect, Form } from 'react-router'
import { getAuth } from '@clerk/react-router/ssr.server'
import { createClerkClient } from '@clerk/react-router/api.server'
import { clerkClient, getAuth } from '@clerk/react-router/server'
import type { Route } from './+types/profile-form'

export async function action(args: Route.ActionArgs) {
Expand All @@ -78,10 +74,8 @@ export async function action(args: Route.ActionArgs) {
const formData = await args.request.formData()
const name = formData.get('name')?.toString()

// Instantiate the JS Backend SDK and get the user's full `Backend User` object
const user = await createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY,
}).users.getUser(userId)
// Get the user's full `Backend User` object
const user = await clerkClient(args).users.getUser(userId)

return {
name,
Expand Down
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1888,6 +1888,10 @@
"title": "Overview",
"href": "/docs/reference/react-router/overview"
},
{
"title": "`clerkMiddleware()`",
"href": "/docs/reference/react-router/clerk-middleware"
},
{
"title": "`rootAuthLoader()`",
"href": "/docs/reference/react-router/root-auth-loader"
Expand Down
4 changes: 4 additions & 0 deletions docs/reference/astro/clerk-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,7 @@ export const onRequest = clerkMiddleware((auth, context) => {
## `clerkMiddleware()` options

<Include src="_partials/clerk-middleware-options" />

### `OrganizationSyncOptions`

<Include src="_partials/organization-sync-options" />
4 changes: 4 additions & 0 deletions docs/reference/express/clerk-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,7 @@ app.listen(PORT, () => {

A flag to enable Clerk's handshake flow, which helps verify the session state when a session JWT has expired. It issues a `307` redirect to refresh the session JWT if the user is still logged in. Defaults to `true`.
</Properties>

#### `OrganizationSyncOptions`

<Include src="_partials/organization-sync-options" />
Loading