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 getmembershipRequests, which is a list of the active organization's membership requests.- membershipRequestsis an object with- datathat contains an array of- OrganizationMembershipRequestobjects.
- Each OrganizationMembershipRequestobject has anaccept()andreject()method to accept or reject the membership request, respectively.
 
- Maps over the dataarray 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 getMembershipRequests()method to retrieve the list of membership requests for the active organization. This method returnsdata, which is an array ofOrganizationMembershipRequestobjects.
- Maps over the dataarray to display the membership requests in a table.
- Provides an "Accept" and "Reject" button for each request that calls the accept()andreject()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.user) {
  // 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