Get started with Organizations
Before you start
Organizations let you group users with Roles and Permissions, enabling you to build multi-tenant B2B apps like Slack (workspaces), Linear (teams), or Vercel (projects) where users can switch between different team contexts. This guide will demonstrate how to add Organizations, create and switch Organizations, and protect routes by Organization and Roles.
Add <OrganizationSwitcher/> to your app
The <OrganizationSwitcher /> component is the easiest way to let users create, switch between, and manage Organizations. It's recommended to place it in your app's header or navigation so it's always accessible to your users. For example:
import {
Show,
UserButton,
SignInButton,
OrganizationSwitcher,
} from '@clerk/tanstack-react-start'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Home,
})
function Home() {
return (
<div>
<h1>Index Route</h1>
<OrganizationSwitcher />
<Show when="signed-out">
<SignInButton />
</Show>
<Show when="signed-in">
<UserButton />
</Show>
</div>
)
}To access information about the currently on the server-side, use clerkClient() to call the getOrganization() method, which returns the Backend Organization object. You'll need to pass an orgId, which you can access from the Auth object.
import { createFileRoute, redirect } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { clerkClient, auth } from '@clerk/tanstack-react-start/server'
const authStateFn = createServerFn({ method: 'GET' }).handler(async () => {
// Use `auth()` to access the `Auth` object
// https://clerk.com/docs/reference/backend/types/auth-object
const { isAuthenticated, orgId, orgRole } = await auth()
// Check if the user is authenticated
if (!isAuthenticated) {
// This might error if you're redirecting to a path that doesn't exist yet
// You can create a sign-in route to handle this
// See https://clerk.com/docs/tanstack-react-start/guides/development/custom-sign-in-or-up-page
throw redirect({
to: '/sign-in/$',
})
}
// Check if there is an Active Organization
if (!orgId) return { orgId: null, orgRole: null, organizationName: null }
// Use the `getOrganization()` method to get the Backend `Organization` object
const organization = await clerkClient().organizations.getOrganization({ organizationId: orgId })
return { orgId, orgRole, organizationName: organization.name }
})
export const Route = createFileRoute('/')({
component: Home,
beforeLoad: () => authStateFn(),
loader: async ({ context }) => {
return {
orgId: context.orgId,
orgRole: context.orgRole,
organizationName: context.organizationName,
}
},
})
function Home() {
const state = Route.useLoaderData()
if (!state.organizationName) return <p>Set an Active Organization to access this page.</p>
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">
Welcome to the <strong>{state.organizationName}</strong> Organization
</h1>
<p className="mb-6">
Your Role in this Organization: <strong>{state.orgRole}</strong>
</p>
</div>
)
}Use the useOrganization() hook to access information about the currently , including the current user's membership and Role.
import { useOrganization } from '@clerk/tanstack-react-start'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Home,
})
function Home() {
const { organization, membership } = useOrganization()
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">
Welcome to the <strong>{organization?.name}</strong> Organization
</h1>
<p className="mb-6">
Your Role in this Organization: <strong>{membership?.role}</strong>
</p>
</div>
)
}Visit your app
Run your project with the following command:
npm run devpnpm run devyarn devbun run devVisit your app locally at localhost:3000.
When you visit your app, Clerk will prompt you to enable Organizations.
Enable Organizations
When prompted, select Enable Organizations. Choose to make membership required.
Create first user and Organization
You must sign in to use Organizations. When prompted, select Sign in to continue. Then, authenticate to create your first user.
Since you chose to make membership required when you enabled Organizations, every user must be in at least one Organization. to create an Organization for your user.
Create and switch Organizations
At this point, Clerk should have redirected you to a page with the <OrganizationSwitcher /> component. This component allows you to create, switch between, and manage Organizations.
- Select the
<OrganizationSwitcher />component, then Create an organization. - Enter
Acme Corpas the Organization name. - Invite users to your Organization and select their Role.
Protect routes by Organization and Roles
You can protect content and even entire routes based on Organization membership, Roles, and Permissions by performing .
In the following example, the page is protected from unauthenticated users, users that don't have the org:admin Role, and users that are not in the Acme Corp Organization. It uses the has() helper to perform the authorization check for the org:admin Role.
import { createFileRoute, redirect } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { clerkClient, auth } from '@clerk/tanstack-react-start/server'
const authStateFn = createServerFn({ method: 'GET' }).handler(async () => {
// Use `auth()` to access the `Auth` object
// https://clerk.com/docs/reference/backend/types/auth-object
const { isAuthenticated, orgId, has } = await auth()
const requiredOrgName = 'Acme Corp'
// Check if the user is authenticated
if (!isAuthenticated) {
// This might error if you're redirecting to a path that doesn't exist yet
// You can create a sign-in route to handle this
// See https://clerk.com/docs/tanstack-react-start/guides/development/custom-sign-in-or-up-page
throw redirect({
to: '/sign-in/$',
})
}
// Check if there is an Active Organization
if (!orgId) return { error: 'Set an Active Organization to access this page.' }
// Check if the user has the `org:admin` Role
if (!has({ role: 'org:admin' })) return { error: 'You must be an admin to access this page.' }
// Use the `getOrganization()` method to get the Backend `Organization` object
const organization = await clerkClient().organizations.getOrganization({ organizationId: orgId })
// Check if Organization name matches (e.g. 'Acme Corp')
if (organization.name !== requiredOrgName) {
return {
error: `This page is only accessible in the ${requiredOrgName} Organization. Switch to the ${requiredOrgName} Organization to access this page.`,
}
}
return { organizationName: organization.name }
})
export const Route = createFileRoute('/protected')({
component: Protected,
beforeLoad: () => authStateFn(),
loader: async ({ context }) => context,
})
function Protected() {
const state = Route.useLoaderData()
if ('error' in state) return <p>{state.error}</p>
return (
<p>
You are currently signed in as an <strong>admin</strong> in the{' '}
<strong>{state.organizationName}</strong> Organization.
</p>
)
}import { useAuth, useOrganization } from '@clerk/tanstack-react-start'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/protected')({
component: Protected,
})
function Protected() {
// The `useAuth()` hook gives you access to properties like `isSignedIn` and `has()`
const { isSignedIn, has } = useAuth()
const { organization } = useOrganization()
const requiredOrgName = 'Acme Corp'
// Check if the user is authenticated
if (!isSignedIn) return <p>You must be signed in to access this page.</p>
// Check if there is an Active Organization
if (!organization) return <p>Set an Active Organization to access this page.</p>
// Check if the user has the `org:admin` Role
if (!has({ role: 'org:admin' })) return <p>You must be an admin to access this page.</p>
// Check if Organization name matches (e.g. 'Acme Corp')
if (organization.name !== requiredOrgName)
return (
<p>
This page is only accessible in the <strong>{requiredOrgName}</strong> Organization. Switch
to the <strong>{requiredOrgName}</strong> Organization to access this page.
</p>
)
return (
<p>
You are currently signed in as an <strong>admin</strong> in the{' '}
<strong>{organization.name}</strong> Organization.
</p>
)
}Navigate to localhost:3000/protected. You should see a green message confirming you are an admin in Acme Corp. Use the <OrganizationSwitcher/> to switch Organizations or rename the Organization to show the red message.
Learn more about protecting routes and checking Organization Roles in the authorization guide.
It's time to build your B2B SaaS!
You've added Clerk Organizations to your app 🎉.
Here are some next steps you can take to scale your app:
-
Control access with Custom Roles and Permissions: define granular Permissions for different user types within Organizations.
-
Onboard entire companies with Verified Domains: automatically invite users with approved email domains (e.g.
@company.com) to join Organizations without manual invitations. -
Enable Enterprise SSO with SAML and OIDC: let customers authenticate through their identity provider (e.g. Okta, Entra ID, Google Workspace) with unlimited connections, no per-connection fees.
Feedback
Last updated on