Build a custom flow for managing a user's organization invitations
This guide will demonstrate how to use the Clerk API to build a custom flow for managing a user's organization invitations.
The following example:
- Uses the
useOrganizationList()
hook to getuserInvitations
, which is a list of the user's organization invitations.userInvitations
is an object withdata
that contains an array ofUserOrganizationInvitation
objects.- Each
UserOrganizationInvitation
object has anaccept()
method that accepts the invitation to the organization.
- Maps over the
data
array to display the invitations in a table, providing an "Accept" button for each invitation that calls theaccept()
method.
This example is written for Next.js App Router but can be adapted for any React-based framework.
'use client'
import { useOrganizationList } from '@clerk/clerk-react'
import React from 'react'
export default function UserInvitationsList() {
const { isLoaded, userInvitations } = useOrganizationList({
userInvitations: {
infinite: true,
keepPreviousData: true,
},
})
if (!isLoaded || userInvitations.isLoading) {
return <>Loading</>
}
return (
<>
<h1>Organization invitations</h1>
<table>
<thead>
<tr>
<th>Email</th>
<th>Organization name</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{userInvitations.data?.map((invitation) => (
<tr key={invitation.id}>
<td>{invitation.emailAddress}</td>
<td>{invitation.publicOrganizationData.name}</td>
<td>{invitation.role}</td>
<td>
<button onClick={() => invitation.accept()}>Accept</button>
</td>
</tr>
))}
</tbody>
</table>
<button disabled={!userInvitations.hasNextPage} onClick={userInvitations.fetchNext}>
Load more
</button>
</>
)
}
The following example:
- Calls the
getOrganizationInvitations()
method to retrieve the list of organization invitations for the active user. This method returnsdata
, which is an array ofUserOrganizationInvitation
objects. - Maps over the
data
array to display the invitations in a table. - Provides an "Accept" button for each invitation that calls the
accept()
method.
Use the following tabs to view the code necessary for the index.html
and main.js
files.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<h1>Organization invitations</h1>
<table>
<thead>
<tr>
<th>Email</th>
<th>Organization name</th>
<th>Role</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="invitations-table-body"></tbody>
</table>
<script type="module" src="/src/main.js" async crossorigin="anonymous"></script>
</body>
</html>
import { Clerk } from '@clerk/clerk-js'
const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!pubKey) {
throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file')
}
const clerk = new Clerk('YOUR_PUBLISHABLE_KEY')
await clerk.load()
if (clerk.user) {
const { data } = await clerk.user.getOrganizationInvitations()
const invitations = data
invitations.map((invitation) => {
const tableBody = document.getElementById('invitations-table-body')
const row = tableBody.insertRow()
row.insertCell().textContent = invitation.emailAddress
row.insertCell().textContent = invitation.publicOrganizationData.name
row.insertCell().textContent = invitation.role
row.insertCell().textContent = invitation.status
// Show accept button for pending invitations
if (invitation.status === 'pending') {
const acceptBtn = document.createElement('button')
acceptBtn.textContent = 'Accept'
acceptBtn.addEventListener('click', async function (e) {
e.preventDefault()
await invitation.accept()
})
row.insertCell().appendChild(acceptBtn)
}
})
} else {
// If there is no active user, mount Clerk's <SignIn />
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
clerk.mountSignIn(signInDiv)
}
Feedback
Last updated on