Build a custom flow for managing organization membership requests
This guide will demonstrate how to use the Clerk API to build a custom flow for managing organization membership requests.
The following example:
- Uses the useOrganization() hook to get
membershipRequests
, which is a list of the membership requests.membershipRequests
is an object withdata
that contains an array of objects.- Each
OrganizationMembershipRequest
object has an and method to accept or reject the membership request, respectively.
- Maps over the
data
array to display the membership requests in a table, providing an "Accept" and "Reject" button for each request that calls theaccept()
andreject()
methods, respectively.
This example is written for Next.js App Router but can be adapted for any React-based framework.
'use client' import { useOrganization } from '@clerk/nextjs' export const MembershipRequestsParams = { membershipRequests: { pageSize: 5, keepPreviousData: true, }, } // List of organization membership requests. export const MembershipRequests = () => { const { isLoaded, membershipRequests } = useOrganization(MembershipRequestsParams) if (!isLoaded) { return <>Loading</> } return ( <> <h1>Membership requests</h1> <table> <thead> <tr> <th>User</th> <th>Date requested</th> <th>Actions</th> </tr> </thead> <tbody> {membershipRequests?.data?.map((mem) => ( <tr key={mem.id}> <td>{mem.publicUserData.identifier}</td> <td>{mem.createdAt.toLocaleDateString()}</td> <td> <button onClick={async () => { await mem.accept() }} > Accept </button> <button onClick={async () => { await mem.reject() }} > Reject </button> </td> </tr> ))} </tbody> </table> <div> <button disabled={!membershipRequests?.hasPreviousPage || membershipRequests?.isFetching} onClick={() => membershipRequests?.fetchPrevious?.()} > Previous </button> <button disabled={!membershipRequests?.hasNextPage || membershipRequests?.isFetching} onClick={() => membershipRequests?.fetchNext?.()} > Next </button> </div> </> ) }
The following example:
- Calls the method to retrieve the list of membership requests for the active organization. This method returns
data
, which is an array of objects. - Maps over the
data
array to display the membership requests in a table. - Provides an "Accept" and "Reject" button for each request that calls the and methods, respectively.
Use the 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>Membership Requests</h1> <table> <thead> <tr> <th>User</th> <th>Date requested</th> <th>Accept</th> <th>Reject</th> </tr> </thead> <tbody id="requests-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.isSignedIn) { // Check for an active organization if (clerk.organization) { const requestsTable = document.getElementById('requests-table-body') const { data } = await clerk.organization .getMembershipRequests() .then((res) => console.log(`Membership requests:`, data).catch((err) => console.error(err))) const requests = data requests.map((request) => { const row = requestsTable.insertRow() row.insertCell().textContent = request.publicUserData.identifier row.insertCell().textContent = request.createdAt.toLocaleDateString() // Accept request const acceptBtn = document.createElement('button') acceptBtn.textContent = 'Accept' acceptBtn.addEventListener('click', async function (e) { e.preventDefault() await request.accept() }) row.insertCell().appendChild(acceptBtn) // Reject request const rejectBtn = document.createElement('button') rejectBtn.textContent = 'Reject' rejectBtn.addEventListener('click', async function (e) { e.preventDefault() await request.reject() }) row.insertCell().appendChild(rejectBtn) }) } else { // If there is no active organization, // mount Clerk's <OrganizationSwitcher /> // to allow the user to set an organization as active document.getElementById('app').innerHTML = ` <h2>Select an organization to set it as active</h2> <div id="org-switcher"></div> ` const orgSwitcherDiv = document.getElementById('org-switcher') clerk.mountOrganizationSwitcher(orgSwitcherDiv) } } 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