Docs

Build a custom flow for viewing a user's organization memberships

Warning

This guide is for users who want to build a custom user interface using the Clerk API. To use a prebuilt UI, use the Account Portal pages or prebuilt components.

This guide will demonstrate how to use the Clerk API to build a custom flow for viewing the list of a user's organization memberships.

The following example:

  1. Uses the useOrganizationList() hook to get memberships, which is a list of the current user's organization memberships. memberships returns data, which is an array of OrganizationMembership objects.
  2. Maps over the data array to display the user's organization memberships in a table.

This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix.

app/components/JoinedOrganizations.tsx
'use client'

import { useOrganizationList, useUser } from '@clerk/nextjs'

export const userMembershipsParams = {
  memberships: {
    pageSize: 5,
    keepPreviousData: true,
  },
}

// List of the user's organization memberships.
export const JoinedOrganizations = () => {
  const { user } = useUser()
  const { isLoaded, userMemberships } = useOrganizationList({
    userMemberships: userMembershipsParams,
  })

  if (!isLoaded) {
    return <>Loading</>
  }

  return (
    <>
      <h1>Joined organizations</h1>
      <table>
        <thead>
          <tr>
            <th>Identifier</th>
            <th>Organization</th>
            <th>Joined</th>
            <th>Role</th>
          </tr>
        </thead>
        <tbody>
          {userMemberships?.data?.map((mem) => (
            <tr key={mem.id}>
              <td>{mem.publicUserData.identifier}</td>
              <td>{mem.organization.name}</td>
              <td>{mem.createdAt.toLocaleDateString()}</td>
              <td>{mem.role}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <div>
        <button
          disabled={!userMemberships?.hasPreviousPage || userMemberships?.isFetching}
          onClick={() => userMemberships?.fetchPrevious?.()}
        >
          Previous
        </button>

        <button
          disabled={!userMemberships?.hasNextPage || userMemberships?.isFetching}
          onClick={() => userMemberships?.fetchNext?.()}
        >
          Next
        </button>
      </div>
    </>
  )
}

The following example:

  1. Calls the getOrganizationMemberships() method to retrieve the list of organizations the current user is a part of. This method returns data, which is an array of OrganizationMembership objects.
  2. Maps over the data array to display the user's organization memberships in a table.

Use the tabs to view the code necessary for the index.html and main.js files.

index.html
<!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>

    <h2>Joined organizations</h2>
    <table id="memberships_table">
      <thead>
        <tr>
          <th>Identifier</th>
          <th>Organization</th>
          <th>Joined</th>
          <th>Role</th>
        </tr>
      </thead>
      <tbody id="memberships_table_body"></tbody>
    </table>

    <script type="module" src="/src/main.js" async crossorigin="anonymous"></script>
  </body>
</html>
main.js
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 { data } = await clerk.user.getOrganizationMemberships()
    const memberships = data

    memberships.map((membership) => {
      const membershipTable = document.getElementById('memberships_table')
      const row = membershipTable.insertRow()
      row.insertCell().textContent = membership.publicUserData.identifier
      row.insertCell().textContent = membership.organization.name
      row.insertCell().textContent = membership.createdAt.toLocaleDateString()
      row.insertCell().textContent = membership.role
    })
  } 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

What did you think of this content?

Last updated on