Clerk logo

Clerk Docs

Ctrl + K
Go to clerkstage.dev
Check out a preview of our new docs.

Manage Member Roles

Learn how to manage member roles in an organization

Overview

Clerk provides the ability for the currently signed-in user to view all the organizations they are members of. In React and Next.js applications, this is done with the useOrganizationList() hook, which returns organizationList.

Organization administrators are able to manage the member roles within an organization. Admins can change the role of a member and also remove a member from an organization.

Usage

View all organizations the current user belongs to

1
// Lists all organization the user is a member of.
2
// Each entry can be a link to a page to manage organization
3
// members.
4
5
// If you create a new Organization, this list will update automatically.
6
import { useOrganizationList } from "@clerk/nextjs";
7
import Link from "next/link";
8
9
const OrganizationList = () => {
10
const { organizationList, isLoaded } = useOrganizationList();
11
12
if (!isLoaded) {
13
// Any loading state
14
return null;
15
}
16
17
return (
18
<div>
19
<h2>Your organizations</h2>
20
{organizationList.length === 0 ? (
21
<div>You do not belong to any organizations yet.</div>
22
) : (
23
<ul>
24
{organizationList.map(({ organization }) => (
25
<li key={organization.id}>
26
<Link
27
href={`/organizations/${organization.id}`}
28
>
29
<a>{organization.name}</a>
30
</Link>
31
</li>
32
))}
33
</ul>
34
)}
35
</div>
36
);
37
};
1
// Lists all organization the user is a member of.
2
// Each entry can be a link to a page to manage organization
3
// members.
4
5
// If you create a new Organization, this list will update automatically.
6
import { useOrganizationList } from "@clerk/clerk-react";
7
8
const OrganizationList = () => {
9
const { organizationList, isLoaded } = useOrganizationList();
10
11
if (!isLoaded) {
12
// Any loading state
13
return null;
14
}
15
16
return (
17
<div>
18
<h2>Your organizations</h2>
19
{organizationList.length === 0 ? (
20
<div>You do not belong to any organizations yet.</div>
21
) : (
22
<ul>
23
{organizationList.map(({ organization }) => (
24
<li key={organization.id}>
25
<a href={`/organizations/${organization.id}`}>{organization.name}</a>
26
</li>
27
))}
28
</ul>
29
)}
30
</div>
31
);
32
};
1
<ul id="organizations_list"></ul>
2
3
<script>
4
const list = document.getElementById("organizations_list");
5
try {
6
const organizationMemberships = await window.Clerk.getOrganizationMemberships();
7
organizationMemberships.map((membership) => {
8
const li = document.createElement("li");
9
li.textContent = `${membership.organization.name} - ${membership.role}`;
10
list.appendChild(li);
11
});
12
} catch (err) {
13
console.error(err);
14
}
15
</script>
16

Manage organization members

1
// pages/organizations/[id].ts
2
import { useState, useEffect } from "react";
3
import { useOrganization } from "@clerk/nextjs";
4
import type { OrganizationMembershipResource } from "@clerk/types";
5
6
// View and manage active organization members, along with any
7
// pending invitations.
8
// Invite new members.
9
export default function Organization() {
10
const {
11
organization: currentOrganization,
12
membership,
13
isLoaded,
14
} = useOrganization();
15
16
if (!isLoaded || !currentOrganization) {
17
return null;
18
}
19
20
const isAdmin = membership.role === "admin";
21
return (
22
<>
23
<h1>Organization: {currentOrganization.name}</h1>
24
<MemberList />
25
{isAdmin && <InvitationList />}
26
</>
27
);
28
}
29
30
// List of organization memberships. Administrators can
31
// change member roles or remove members from the organization.
32
function MemberList() {
33
const { membershipList, membership } = useOrganization({
34
membershipList: {},
35
});
36
37
if (!membershipList) {
38
return null;
39
}
40
41
const isCurrentUserAdmin = membership.role === "admin";
42
43
return (
44
<div>
45
<h2>Organization members</h2>
46
<ul>
47
{membershipList.map((m) => (
48
<li key={m.id}>
49
{m.publicUserData.firstName} {m.publicUserData.lastName} &lt;
50
{m.publicUserData.identifier}&gt; :: {m.role}
51
{isCurrentUserAdmin && <AdminControls membership={m} />}
52
</li>
53
))}
54
</ul>
55
</div>
56
);
57
}
58
59
function AdminControls({
60
membership,
61
}: {
62
membership: OrganizationMembershipResource;
63
}){
64
const [disabled, setDisabled] = useState(false);
65
const {
66
user: { id: userId },
67
} = useUser();
68
69
if (membership.publicUserData.userId === userId) {
70
return null;
71
}
72
73
const remove = async () => {
74
setDisabled(true);
75
await membership.destroy();
76
};
77
78
const changeRole = async (role: MembershipRole) => {
79
setDisabled(true);
80
await membership.update({ role });
81
setDisabled(false);
82
};
83
84
return (
85
<>
86
::{" "}
87
<button disabled={disabled} onClick={remove}>
88
Remove member
89
</button>{" "}
90
{membership.role === "admin" ? (
91
<button disabled={disabled} onClick={() => changeRole("basic_member")}>
92
Change to member
93
</button>
94
) : (
95
<button disabled={disabled} onClick={() => changeRole("admin")}>
96
Change to admin
97
</button>
98
)}
99
</>
100
);
101
};
102
103
// List of organization pending invitations.
104
// You can invite new organization members and
105
// revoke already sent invitations.
106
function InvitationList() {
107
const { invitationList } = useOrganization();
108
109
if (!invitationList) {
110
return null;
111
}
112
113
const revoke = async (inv) => {
114
await inv.revoke();
115
};
116
117
return (
118
<div>
119
<h2>Invite member</h2>
120
<InviteMember />
121
122
<h2>Pending invitations</h2>
123
<ul>
124
{invitationList.map((i) => (
125
<li key={i.id}>
126
{i.emailAddress} <button onClick={() => revoke(i)}>Revoke</button>
127
</li>
128
))}
129
</ul>
130
</div>
131
);
132
}
133
134
function InviteMember(){
135
const { organization } = useOrganization();
136
const [emailAddress, setEmailAddress] = useState("");
137
const [role, setRole] = useState<"basic_member" | "admin">("basic_member");
138
const [disabled, setDisabled] = useState(false);
139
140
const onSubmit = async (e) => {
141
e.preventDefault();
142
setDisabled(true);
143
await organization.inviteMember({ emailAddress, role });
144
setEmailAddress("");
145
setRole("basic_member");
146
setDisabled(false);
147
};
148
149
return (
150
<form onSubmit={onSubmit}>
151
<input
152
type="text"
153
placeholder="Email address"
154
value={emailAddress}
155
onChange={(e) => setEmailAddress(e.target.value)}
156
/>
157
<label>
158
<input
159
type="radio"
160
checked={role === "admin"}
161
onChange={() => {
162
setRole("admin");
163
}}
164
/>{" "}
165
Admin
166
</label>
167
<label>
168
<input
169
type="radio"
170
checked={role === "basic_member"}
171
onChange={() => {
172
setRole("basic_member");
173
}}
174
/>{" "}
175
Member
176
</label>{" "}
177
<button type="submit" disabled={disabled}>
178
Invite
179
</button>
180
</form>
181
);
182
};
183
1
import { useState, useEffect } from "react";
2
import { useOrganization } from "@clerk/clerk-react";
3
import type { OrganizationMembershipResource } from "@clerk/types";
4
5
// View and manage active organization members, along with any
6
// pending invitations.
7
// Invite new members.
8
export default function Organization() {
9
const {
10
organization: currentOrganization,
11
membership,
12
isLoaded,
13
} = useOrganization();
14
15
if (!isLoaded || !currentOrganization) {
16
return null;
17
}
18
19
const isAdmin = membership.role === "admin";
20
return (
21
<>
22
<h1>Organization: {currentOrganization.name}</h1>
23
<MemberList />
24
{isAdmin && <InvitationList />}
25
</>
26
);
27
}
28
29
// List of organization memberships. Administrators can
30
// change member roles or remove members from the organization.
31
function MemberList() {
32
const { membershipList, membership } = useOrganization({
33
membershipList: {},
34
});
35
36
if (!membershipList) {
37
return null;
38
}
39
40
const isCurrentUserAdmin = membership.role === "admin";
41
42
return (
43
<div>
44
<h2>Organization members</h2>
45
<ul>
46
{membershipList.map((m) => (
47
<li key={m.id}>
48
{m.publicUserData.firstName} {m.publicUserData.lastName} &lt;
49
{m.publicUserData.identifier}&gt; :: {m.role}
50
{isCurrentUserAdmin && <AdminControls membership={m} />}
51
</li>
52
))}
53
</ul>
54
</div>
55
);
56
}
57
58
function AdminControls({
59
membership,
60
}: {
61
membership: OrganizationMembershipResource;
62
}){
63
const [disabled, setDisabled] = useState(false);
64
const {
65
user: { id: userId },
66
} = useUser();
67
68
if (membership.publicUserData.userId === userId) {
69
return null;
70
}
71
72
const remove = async () => {
73
setDisabled(true);
74
await membership.destroy();
75
};
76
77
const changeRole = async (role: MembershipRole) => {
78
setDisabled(true);
79
await membership.update({ role });
80
setDisabled(false);
81
};
82
83
return (
84
<>
85
::{" "}
86
<button disabled={disabled} onClick={remove}>
87
Remove member
88
</button>{" "}
89
{membership.role === "admin" ? (
90
<button disabled={disabled} onClick={() => changeRole("basic_member")}>
91
Change to member
92
</button>
93
) : (
94
<button disabled={disabled} onClick={() => changeRole("admin")}>
95
Change to admin
96
</button>
97
)}
98
</>
99
);
100
};
101
102
// List of organization pending invitations.
103
// You can invite new organization members and
104
// revoke already sent invitations.
105
function InvitationList() {
106
const { invitationList } = useOrganization();
107
108
if (!invitationList) {
109
return null;
110
}
111
112
const revoke = async (inv) => {
113
await inv.revoke();
114
};
115
116
return (
117
<div>
118
<h2>Invite member</h2>
119
<InviteMember />
120
121
<h2>Pending invitations</h2>
122
<ul>
123
{invitationList.map((i) => (
124
<li key={i.id}>
125
{i.emailAddress} <button onClick={() => revoke(i)}>Revoke</button>
126
</li>
127
))}
128
</ul>
129
</div>
130
);
131
}
132
133
function InviteMember(){
134
const { organization } = useOrganization();
135
const [emailAddress, setEmailAddress] = useState("");
136
const [role, setRole] = useState<"basic_member" | "admin">("basic_member");
137
const [disabled, setDisabled] = useState(false);
138
139
const onSubmit = async (e) => {
140
e.preventDefault();
141
setDisabled(true);
142
await organization.inviteMember({ emailAddress, role });
143
setEmailAddress("");
144
setRole("basic_member");
145
setDisabled(false);
146
};
147
148
return (
149
<form onSubmit={onSubmit}>
150
<input
151
type="text"
152
placeholder="Email address"
153
value={emailAddress}
154
onChange={(e) => setEmailAddress(e.target.value)}
155
/>
156
<label>
157
<input
158
type="radio"
159
checked={role === "admin"}
160
onChange={() => {
161
setRole("admin");
162
}}
163
/>{" "}
164
Admin
165
</label>
166
<label>
167
<input
168
type="radio"
169
checked={role === "basic_member"}
170
onChange={() => {
171
setRole("basic_member");
172
}}
173
/>{" "}
174
Member
175
</label>{" "}
176
<button type="submit" disabled={disabled}>
177
Invite
178
</button>
179
</form>
180
);
181
};
182
1
<ul id="memberships_list"></ul>
2
<ul id="invitations_list"></ul>
3
4
<form id="new_invitation">
5
<div>
6
<label>Email address</div>
7
<br />
8
<input type="email" name="email_address" />
9
</div>
10
<button>Invite</button>
11
</form>
12
13
<script>
14
async function renderMemberships(organization, isAdmin) {
15
const list = document.getElementById("memberships_list");
16
try {
17
const memberships = await organization.getMembers();
18
memberships.map((membership) => {
19
const li = document.createElement("li");
20
li.textContent = `${membership.identifier} - ${membership.role}`;
21
22
// Add administrative actions; update role and remove member.
23
if (isAdmin) {
24
const updateBtn = document.createElement("button");
25
updateBtn.textContent = "Change role";
26
updateBtn.addEventListener("click", async function(e) {
27
e.preventDefault();
28
const role = membership.role === "admin" ?
29
"basic_member" :
30
"admin";
31
await membership.update({ role });
32
});
33
li.appendChild(updateBtn);
34
35
const removeBtn = document.createElement("button");
36
removeBtn.textContent = "Remove";
37
removeBtn.addEventListener("click", async function(e) {
38
e.preventDefault();
39
await currentOrganization.removeMember(membership.userId);
40
});
41
li.appendChild(removeBtn);
42
}
43
44
// Add the entry to the list
45
list.appendChild(li);
46
});
47
} catch (err) {
48
console.error(err);
49
}
50
}
51
52
async function renderInvitations(organization, isAdmin) {
53
const list = document.getElementById("invitations_list");
54
try {
55
const invitations = await organization.getPendingInvitations();
56
invitations.map((invitation) => {
57
const li = document.createElement("li");
58
li.textContent = `${invitation.emailAddress} - ${invitation.role}`;
59
60
// Add administrative actions; revoke invitation
61
if (isAdmin) {
62
const revokeBtn = document.createElement("button");
63
revokeBtn.textContent = "Revoke";
64
revokeBtn.addEventListener("click", async function(e) {
65
e.preventDefault();
66
await invitation.revoke();
67
});
68
li.appendChild(revokeBtn);
69
}
70
// Add the entry to the list
71
list.appendChild(li);
72
});
73
} catch (err) {
74
console.error(err);
75
}
76
}
77
78
async function init() {
79
// This is the current organization ID.
80
const organizationId = "org_XXXXXXX";
81
const organizationMemberships = await window.Clerk.getOrganizationMemberships()
82
const currentMembership = organizationMemberships.find(membership
83
=> membership.organization.id === organizationId);
84
const currentOrganization = currentMembership.organization;
85
86
if (!currentOrganization) {
87
return;
88
}
89
const isAdmin = currentMembership.role === "admin";
90
91
renderMemberships(currentOrganization, isAdmin);
92
renderInvitations(currentOrganization, isAdmin);
93
94
if (isAdmin) {
95
const form = document.getElementById("new_invitation");
96
form.addEventListener("submit", async function(e) {
97
e.preventDefault();
98
const inputEl = form.getElementsByTagName("input");
99
if (!inputEl) {
100
return;
101
}
102
103
try {
104
await currentOrganization.inviteMember({
105
emailAddress: inputEl.value,
106
role: "basic_member",
107
});
108
} catch (err) {
109
console.error(err);
110
}
111
});
112
}
113
}
114
115
init();
116
</script>
117

Was this helpful?

Clerk © 2023