Organizations
Learn how to manage organizations and their members
Organizations is a premium feature. Please get in touch if you would like us to enable it for your account. You can contact us at support@clerk.dev.
Demo Repo
A good way to explore Clerk's organizations functionality is to check out this demo repo:
https://github.com/clerkinc/organizations-demo
Overview
Organizations are shared accounts, useful for project and team leaders. Organization members can usually collaborate across shared resources. There are members with elevated privileges who can manage member access to the organization's data and resources.
Each member of an organization needs to have a user account in your application. All organization members have access to most of the organization resources, but some members can take advantage of administrative features.
Roles
This distinction in member permissions is possible with organization member roles. Roles determine a user's level of access to the organization. There are currently two roles; administrators and members.
- admin - The "admin" role offers full access to organization resources. Members with the admin role have administrator privileges and can fully manage organizations and organization memberships.
- basic_member - The "basic_member" role is the standard role for a user that is part of the organization. Access to organization resources is limited. Basic members cannot manage organizations and organization memberships, but can view information about the organization and other members in it.
Available actions
You can use Clerk's organizations feature to provide team grouping and sharing functionality for users of your applications.
Your users can create organizations. Organization owners effectively get the "admin" role.
Administrators can then invite other users to join the organization. An invitation email is sent out, and organization invitations support adding existing users of your application, or new ones. They can register once they accept the invitation.
Administrators can also revoke an invitation for a user that hasn't joined yet, as well as remove a user who's already a member from the organization or change their role. When removing organization members or updating their role, there needs to be at least one administrator for the organization at all times.
Administrators can also update an organization, in order to change the organization name for example.
Finally, all members of an organization, regardless of their role can view information about other members in the same organization.
Before you start
- You need to create a Clerk Application in your Clerk Dashboard. For more information, check out our Setup your application guide.
- You need to install Clerk React or ClerkJS to your application.
- The organizations feature needs to be enabled for your account. Contact us at support@clerk.dev to turn the feature on.
Custom flow
Let's follow a simple scenario for setting up an organization and managing its members.
In the following guide, we'll create a new organization, update it and then invite a couple of members. We'll see how to revoke one of the two invitations, and update the other member's role. Finally, we'll remove the other member from the organization.
This tutorial assumes that a signed in user already exists. All snippets below require a session and user to be present.
Create a new organization
1// pages/organizations/new.js2import { useState } from "react";3import { useRouter } from "next/router";4import { useOrganizations } from "@clerk/nextjs";56// Form to create a new organization. The current user7// will become the organization administrator.8export default function NewOrganization() {9const [name, setName] = useState("");10const router = useRouter();1112const { createOrganization } = useOrganizations();1314async function submit(e) {15e.preventDefault();16try {17// Create a new organization.18await createOrganization({ name });19setName("");20router.push("/organizations");21} catch (err) {22console.error(err);23}24}2526return (27<div>28<h2>Create an organization</h2>29<form onSubmit={submit}>30<div>31<label>Name</label>32<br />33<input34name="name"35value={name}36onChange={(e) => setName(e.target.value)}37/>38</div>39<button>Create organization</button>40</form>41</div>42);43}
View all organizations the current user belongs to
1import { useState, useEffect } from "react";2import Link from "next/link";3import { useOrganizations } from "@clerk/nextjs";4import { OrganizationMembershipResource } from "@clerk/types";56// Lists all organization the user is a member of.7// Each entry is a link to a page to manage organization8// members.9export default function Organizations() {10const [organizationMemberships, setOrganizationMemberships] = useState<11OrganizationMembershipResource[]12>([]);1314const { getOrganizationMemberships } = useOrganizations();1516useEffect(() => {17async function fetchOrganizationMemberships() {18try {19const orgs = await getOrganizationMemberships();20setOrganizationMemberships(orgs);21} catch (err) {22console.error(err);23}24}2526fetchOrganizationMemberships();27}, []);2829return (30<div>31<h2>Your organizations</h2>32<ul>33{organizationMemberships.map(({ organization }) => (34<Link35key={organization.id}36href={`/organizations/${organization.id}`}37>38{organization.name}39</Link>40))}41</ul>42</div>43);44}4546
Update the organization name
1// pages/organizations/[id]/edit.js2import { useState, useEffect } from "react";3import { useRouter } from "next/router";4import { useOrganizations } from "@clerk/nextjs";56export default function EditOrganization() {7const [organization, setOrganization] = useState(null);8const [name, setName] = useState("");910const { query } = useRouter();11const organizationId = query.id;1213const { getOrganization } = useOrganizations();1415useEffect(() => {16async function fetchOrganization() {17try {18const org = await getOrganization(organizationId);19setOrganization(org);20} catch (err) {21console.log(err);22}23}2425fetchOrganization();26}, [organizationId, getOrganization]);2728useEffect(() => {29if (!organization) {30return;31}32setName(organization.name);33}, [organization]);3435async function submit(e) {36e.preventDefault();37try {38await organization.update({ name });39router.push(`/organizations/${organization.id}`);40} catch (err) {41console.error(err);42}43}4445if (!organization) {46return null;47}4849return (50<div>51<h2>Edit organization</h2>52<form onSubmit={submit}>53<div>54<label>Name</label>55<br />56<input57name="name"58value={name}59onChange={(e) => setName(e.target.value)}60/>61</div>62<button>Save</button>63</form>64</div>65);66}
Manage organization members
1// pages/organizations/[id].js2import { useState, useEffect } from "react";3import { useRouter } from "next/router";4import { useOrganizations } from "@clerk/nextjs";56// View and manage organization members, along with any7// pending invitations.8// Invite new members.9export default function Organization() {10const [organizationMemberships, setOrganizationMemberships] = useState([]);1112const { query } = useRouter();13const organizationId = query.id;1415const { getOrganizationMemberships } = useOrganizations();1617useEffect(() => {18async function fetchOrganizationMemberships() {19try {20const orgMemberships = await getOrganizationMemberships();21setOrganizationMemberships(orgMemberships);22} catch (err) {23console.log(err);24}25}2627fetchOrganizationMemberships();28}, [organizationId, getOrganizationMemberships]);2930const currentOrganizationMembership = organizationMemberships.find(membership =>31membership.organization.id === organizationId);3233if (!currentOrganizationMembership) {34return null;35}3637const isAdmin = currentOrganizationMembership.role === "admin";3839return (40<div>41<h2>{currentOrganizationMembership.organization.name}</h2>4243<Memberships organization={currentOrganizationMembership.organization} isAdmin={isAdmin} />44{isAdmin && <Invitations organization={currentOrganizationMembership.organization} />}45</div>46);47}4849// List of organization memberships. Administrators can50// change member roles or remove members from the organization.51function Memberships({ organization, isAdmin }) {52const [memberships, setMemberships] = useState([]);5354const getMemberships = organization.getMemberships;5556const fetchMemberships = useCallback(async () => {57try {58const membs = await getMemberships();59setMemberships(membs);60} catch (err) {61console.error(err);62}63}, [getMemberships]);6465useEffect(() => {66fetchMemberships();67}, [fetchMemberships]);6869async function remove(userId) {70try {71await organization.removeMember(userId);72fetchMemberships();73} catch (err) {74console.error(err);75}76}7778async function switchRole(membership) {79const role = membership.role === "admin" ? "basic_member" : "admin";80try {81await membership.update({ role });82fetchMemberships();83} catch (err) {84console.error(err);85}86}8788return (89<div>90<b>Members</b>91<ul>92{memberships.map((membership) => (93<li key={membership.id}>94{membership.publicUserData.identifier} - {membership.role}95{isAdmin && (96<>97<br />98<button99onClick={(e) => {100switchRole(membership);101}}102>103Change role104</button>105 or 106<button107onClick={(e) => {108e.preventDefault();109remove(membership.publicUserData.userId);110}}111>112Remove113</button>114</>115)}116</li>117))}118</ul>119</div>120);121}122123// List of organization pending invitations.124// You can invite new organization members and125// revoke already sent invitations.126function Invitations({ organization }) {127const [invitations, setInvitations] = useState([]);128const [emailAddress, setEmailAddress] = useState("");129130const getPendingInvitations = organization.getPendingInvitations;131132const fetchInvitations = useCallback(async () => {133try {134const invites = await getPendingInvitations();135setInvitations(invites);136} catch (err) {137console.error(err);138}139}, [getPendingInvitations]);140141useEffect(() => {142fetchInvitations();143}, [fetchInvitations]);144145async function invite(e) {146e.preventDefault();147try {148await organization.inviteMember({149emailAddress,150role: "basic_member",151});152setEmailAddress("");153fetchInvitations();154} catch (err) {155console.error(err);156}157}158159async function revoke(invitation) {160try {161await invitation.revoke();162fetchInvitations();163} catch (err) {164console.error(err);165}166}167168return (169<div>170<b>Pending invitations</b>171<ul>172{invitations.map((invitation) => (173<li key={invitation.id}>174{invitation.emailAddress}175 176<button177onClick={(e) => {178e.preventDefault();179revoke(invitation);180}}181>182Revoke183</button>184</li>185))}186</ul>187188<b>Invite new member</b>189<form onSubmit={invite}>190<div>191<label>Email address</label>192<br />193<input194type="email"195name="email_address"196value={emailAddress}197onChange={(e) => setEmailAddress(e.target.value)}198/>199</div>200<button>Invite</button>201</form>202</div>203);204}