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.- userInvitationsis an object with- datathat contains an array of- UserOrganizationInvitationobjects.
- Each UserOrganizationInvitationobject has anaccept()method that accepts the invitation to the organization.
 
- Maps over the dataarray 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 ofUserOrganizationInvitationobjects.
- Maps over the dataarray 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