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

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.js
2
import { useState } from "react";
3
import { useRouter } from "next/router";
4
import { useOrganizations } from "@clerk/nextjs";
5
6
// Form to create a new organization. The current user
7
// will become the organization administrator.
8
export default function NewOrganization() {
9
const [name, setName] = useState("");
10
const router = useRouter();
11
12
const { createOrganization } = useOrganizations();
13
14
async function submit(e) {
15
e.preventDefault();
16
try {
17
// Create a new organization.
18
await createOrganization({ name });
19
setName("");
20
router.push("/organizations");
21
} catch (err) {
22
console.error(err);
23
}
24
}
25
26
return (
27
<div>
28
<h2>Create an organization</h2>
29
<form onSubmit={submit}>
30
<div>
31
<label>Name</label>
32
<br />
33
<input
34
name="name"
35
value={name}
36
onChange={(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

1
import { useState, useEffect } from "react";
2
import Link from "next/link";
3
import { useOrganizations } from "@clerk/nextjs";
4
import { OrganizationMembershipResource } from "@clerk/types";
5
6
// Lists all organization the user is a member of.
7
// Each entry is a link to a page to manage organization
8
// members.
9
export default function Organizations() {
10
const [organizationMemberships, setOrganizationMemberships] = useState<
11
OrganizationMembershipResource[]
12
>([]);
13
14
const { getOrganizationMemberships } = useOrganizations();
15
16
useEffect(() => {
17
async function fetchOrganizationMemberships() {
18
try {
19
const orgs = await getOrganizationMemberships();
20
setOrganizationMemberships(orgs);
21
} catch (err) {
22
console.error(err);
23
}
24
}
25
26
fetchOrganizationMemberships();
27
}, []);
28
29
return (
30
<div>
31
<h2>Your organizations</h2>
32
<ul>
33
{organizationMemberships.map(({ organization }) => (
34
<Link
35
key={organization.id}
36
href={`/organizations/${organization.id}`}
37
>
38
{organization.name}
39
</Link>
40
))}
41
</ul>
42
</div>
43
);
44
}
45
46

Update the organization name

1
// pages/organizations/[id]/edit.js
2
import { useState, useEffect } from "react";
3
import { useRouter } from "next/router";
4
import { useOrganizations } from "@clerk/nextjs";
5
6
export default function EditOrganization() {
7
const [organization, setOrganization] = useState(null);
8
const [name, setName] = useState("");
9
10
const { query } = useRouter();
11
const organizationId = query.id;
12
13
const { getOrganization } = useOrganizations();
14
15
useEffect(() => {
16
async function fetchOrganization() {
17
try {
18
const org = await getOrganization(organizationId);
19
setOrganization(org);
20
} catch (err) {
21
console.log(err);
22
}
23
}
24
25
fetchOrganization();
26
}, [organizationId, getOrganization]);
27
28
useEffect(() => {
29
if (!organization) {
30
return;
31
}
32
setName(organization.name);
33
}, [organization]);
34
35
async function submit(e) {
36
e.preventDefault();
37
try {
38
await organization.update({ name });
39
router.push(`/organizations/${organization.id}`);
40
} catch (err) {
41
console.error(err);
42
}
43
}
44
45
if (!organization) {
46
return null;
47
}
48
49
return (
50
<div>
51
<h2>Edit organization</h2>
52
<form onSubmit={submit}>
53
<div>
54
<label>Name</label>
55
<br />
56
<input
57
name="name"
58
value={name}
59
onChange={(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].js
2
import { useState, useEffect } from "react";
3
import { useRouter } from "next/router";
4
import { useOrganizations } from "@clerk/nextjs";
5
6
// View and manage organization members, along with any
7
// pending invitations.
8
// Invite new members.
9
export default function Organization() {
10
const [organizationMemberships, setOrganizationMemberships] = useState([]);
11
12
const { query } = useRouter();
13
const organizationId = query.id;
14
15
const { getOrganizationMemberships } = useOrganizations();
16
17
useEffect(() => {
18
async function fetchOrganizationMemberships() {
19
try {
20
const orgMemberships = await getOrganizationMemberships();
21
setOrganizationMemberships(orgMemberships);
22
} catch (err) {
23
console.log(err);
24
}
25
}
26
27
fetchOrganizationMemberships();
28
}, [organizationId, getOrganizationMemberships]);
29
30
const currentOrganizationMembership = organizationMemberships.find(membership =>
31
membership.organization.id === organizationId);
32
33
if (!currentOrganizationMembership) {
34
return null;
35
}
36
37
const isAdmin = currentOrganizationMembership.role === "admin";
38
39
return (
40
<div>
41
<h2>{currentOrganizationMembership.organization.name}</h2>
42
43
<Memberships organization={currentOrganizationMembership.organization} isAdmin={isAdmin} />
44
{isAdmin && <Invitations organization={currentOrganizationMembership.organization} />}
45
</div>
46
);
47
}
48
49
// List of organization memberships. Administrators can
50
// change member roles or remove members from the organization.
51
function Memberships({ organization, isAdmin }) {
52
const [memberships, setMemberships] = useState([]);
53
54
const getMemberships = organization.getMemberships;
55
56
const fetchMemberships = useCallback(async () => {
57
try {
58
const membs = await getMemberships();
59
setMemberships(membs);
60
} catch (err) {
61
console.error(err);
62
}
63
}, [getMemberships]);
64
65
useEffect(() => {
66
fetchMemberships();
67
}, [fetchMemberships]);
68
69
async function remove(userId) {
70
try {
71
await organization.removeMember(userId);
72
fetchMemberships();
73
} catch (err) {
74
console.error(err);
75
}
76
}
77
78
async function switchRole(membership) {
79
const role = membership.role === "admin" ? "basic_member" : "admin";
80
try {
81
await membership.update({ role });
82
fetchMemberships();
83
} catch (err) {
84
console.error(err);
85
}
86
}
87
88
return (
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
<button
99
onClick={(e) => {
100
switchRole(membership);
101
}}
102
>
103
Change role
104
</button>
105
&nbsp;or&nbsp;
106
<button
107
onClick={(e) => {
108
e.preventDefault();
109
remove(membership.publicUserData.userId);
110
}}
111
>
112
Remove
113
</button>
114
</>
115
)}
116
</li>
117
))}
118
</ul>
119
</div>
120
);
121
}
122
123
// List of organization pending invitations.
124
// You can invite new organization members and
125
// revoke already sent invitations.
126
function Invitations({ organization }) {
127
const [invitations, setInvitations] = useState([]);
128
const [emailAddress, setEmailAddress] = useState("");
129
130
const getPendingInvitations = organization.getPendingInvitations;
131
132
const fetchInvitations = useCallback(async () => {
133
try {
134
const invites = await getPendingInvitations();
135
setInvitations(invites);
136
} catch (err) {
137
console.error(err);
138
}
139
}, [getPendingInvitations]);
140
141
useEffect(() => {
142
fetchInvitations();
143
}, [fetchInvitations]);
144
145
async function invite(e) {
146
e.preventDefault();
147
try {
148
await organization.inviteMember({
149
emailAddress,
150
role: "basic_member",
151
});
152
setEmailAddress("");
153
fetchInvitations();
154
} catch (err) {
155
console.error(err);
156
}
157
}
158
159
async function revoke(invitation) {
160
try {
161
await invitation.revoke();
162
fetchInvitations();
163
} catch (err) {
164
console.error(err);
165
}
166
}
167
168
return (
169
<div>
170
<b>Pending invitations</b>
171
<ul>
172
{invitations.map((invitation) => (
173
<li key={invitation.id}>
174
{invitation.emailAddress}
175
&nbsp;
176
<button
177
onClick={(e) => {
178
e.preventDefault();
179
revoke(invitation);
180
}}
181
>
182
Revoke
183
</button>
184
</li>
185
))}
186
</ul>
187
188
<b>Invite new member</b>
189
<form onSubmit={invite}>
190
<div>
191
<label>Email address</label>
192
<br />
193
<input
194
type="email"
195
name="email_address"
196
value={emailAddress}
197
onChange={(e) => setEmailAddress(e.target.value)}
198
/>
199
</div>
200
<button>Invite</button>
201
</form>
202
</div>
203
);
204
}

© 2022 Clerk. All rights reserved.