Docs

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

Caution

This guide is for users who want to build a custom user interface using the Clerk API. To view the list of a user's organization memberships using a prebuilt UI, you should use Clerk's <OrganizationList/> component.

This guide will demonstrate how to use Clerk's 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 or Gatsby.

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?