payloadcms

multi tenant

Official
by payloadcms
payload
Roles & permissions
v3
workspace:*
MIT

Overview

Summary generated from the README

Adds multi-tenant support to Payload. It adds a tenant field to selected collections and a tenants array to the users collection, and applies tenant-based access constraints and list filters. Collections can behave like globals per tenant, and access rules can be customized or switched off per collection.

Features

  • Adds a tenant relationship field to every tenant-enabled collection
  • Adds a tenants array field to the users collection, with an option to place it manually
  • Merges tenant constraints into collection access and list filters by default
  • Treats a collection as a per-tenant global with the isGlobal option
  • Grants super-admin users access to all tenants through a configurable function
  • Cleans up related documents and user references after a tenant is deleted

Use cases

  • Run several websites or clients from one Payload instance with separated content
  • Give each tenant its own navigation or settings through collections that act as globals
  • Share selected documents such as media between tenants with custom access control

From the author

Multi Tenant plugin for Payload

Installation

npm i @payloadcms/plugin-multi-tenant

45.4k

Stars

4.3k

Forks

1380

Open Issues

Today

Last Updated

90.4k

Weekly Downloads

v3.91.0

npm Version

428.4 KB

Unpacked Size

0

Dependencies

Repository stars

Trends appear after 7 days of data. Daily snapshots have not started yet.

Shared by 12 plugins in payloadcms/payload.

npm weekly downloads

Trends appear after 7 days of data. Daily snapshots have not started yet.

Each point is npm's rolling 7-day download count.

Health Score

100/100Excellent

Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.

Badge for your README

Payload Directory health badge
Markdown
[![Payload Directory](https://payloaddirectory.dev/badge/official-plugin-multi-tenant.svg)](https://payloaddirectory.dev/plugins/official-plugin-multi-tenant)
HTML
<a href="https://payloaddirectory.dev/plugins/official-plugin-multi-tenant"><img src="https://payloaddirectory.dev/badge/official-plugin-multi-tenant.svg" alt="Payload Directory"></a>
Updated Oct 21, 2026Created Jan 5, 2021Published Oct 15, 2026

README

Multi Tenant Plugin

A plugin for Payload to easily manage multiple tenants from within your admin panel.

Installation

pnpm add @payloadcms/plugin-multi-tenant

Plugin Types

type MultiTenantPluginConfig<ConfigTypes = unknown> = {
  /**
   * After a tenant is deleted, the plugin will attempt to clean up related documents
   * - removing documents with the tenant ID
   * - removing the tenant from users
   *
   * @default true
   */
  cleanupAfterTenantDelete?: boolean
  /**
   * Automatically
   */
  collections: {
    [key in CollectionSlug]?: {
      /**
       * Set to `true` if you want the collection to behave as a global
       *
       * @default false
       */
      isGlobal?: boolean
      /**
       * Set to `false` if you want to manually apply the baseFilter
       *
       * @default true
       */
      useBaseFilter?: boolean
      /**
       * Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied
       *
       * @default true
       */
      useTenantAccess?: boolean
    }
  }
  /**
   * Enables debug mode
   * - Makes the tenant field visible in the admin UI within applicable collections
   *
   * @default false
   */
  debug?: boolean
  /**
   * Enables the multi-tenant plugin
   *
   * @default true
   */
  enabled?: boolean
  /**
   * Field configuration for the field added to all tenant enabled collections
   */
  tenantField?: {
    access?: RelationshipField['access']
    /**
     * The name of the field added to all tenant enabled collections
     *
     * @default 'tenant'
     */
    name?: string
  }
  /**
   * Field configuration for the field added to the users collection
   *
   * If `includeDefaultField` is `false`, you must include the field on your users collection manually
   * This is useful if you want to customize the field or place the field in a specific location
   */
  tenantsArrayField?:
    | {
        /**
         * Access configuration for the array field
         */
        arrayFieldAccess?: ArrayField['access']
        /**
         * Name of the array field
         *
         * @default 'tenants'
         */
        arrayFieldName?: string
        /**
         * Name of the tenant field
         *
         * @default 'tenant'
         */
        arrayTenantFieldName?: string
        /**
         * When `includeDefaultField` is `true`, the field will be added to the users collection automatically
         */
        includeDefaultField?: true
        /**
         * Additional fields to include on the tenants array field
         */
        rowFields?: Field[]
        /**
         * Access configuration for the tenant field
         */
        tenantFieldAccess?: RelationshipField['access']
      }
    | {
        arrayFieldAccess?: never
        arrayFieldName?: string
        arrayTenantFieldName?: string
        /**
         * When `includeDefaultField` is `false`, you must include the field on your users collection manually
         */
        includeDefaultField?: false
        rowFields?: never
        tenantFieldAccess?: never
      }
  /**
   * Customize tenant selector label
   *
   * Either a string or an object where the keys are i18n codes and the values are the string labels
   */
  tenantSelectorLabel?:
    | Partial<{
        [key in AcceptedLanguages]?: string
      }>
    | string
  /**
   * The slug for the tenant collection
   *
   * @default 'tenants'
   */
  tenantsSlug?: string
  /**
   * Function that determines if a user has access to _all_ tenants
   *
   * Useful for super-admin type users
   */
  userHasAccessToAllTenants?: (
    user: ConfigTypes extends { user: unknown } ? ConfigTypes['user'] : User,
  ) => boolean
  /**
   * Opt out of adding access constraints to the tenants collection
   */
  useTenantsCollectionAccess?: boolean
  /**
   * Opt out including the baseListFilter to filter tenants by selected tenant
   */
  useTenantsListFilter?: boolean
  /**
   * Opt out including the baseListFilter to filter users by selected tenant
   */
  useUsersTenantFilter?: boolean
}

How to configure Collections as Globals for multi-tenant

When using multi-tenant, globals need to actually be configured as collections so the content can be specific per tenant. To do that, you can mark a collection with isGlobal and it will behave like a global and users will not see the list view.

multiTenantPlugin({
  collections: {
    navigation: {
      isGlobal: true,
    },
  },
})

Customizing access control

In some cases, the access control supplied out of the box may be too strict. For example, if you need some documents to be shared between tenants, you will need to opt out of the supplied access control functionality.

By default this plugin merges your access control result with a constraint based on tenants the user has access to within an AND condition. That would not work for the above scenario.

In the multi-tenant plugin config you can set useTenantAccess to false:

// File: payload.config.ts

import { buildConfig } from 'payload'
import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'
import { getTenantAccess } from '@payloadcms/plugin-multi-tenant/utilities'
import { Config as ConfigTypes } from './payload-types'

// Add the plugin to your payload config
export default buildConfig({
  plugins: [
    multiTenantPlugin<ConfigTypes>({
      collections: {
        media: {
          useTenantAccess: false,
        },
      },
    }),
  ],
  collections: [
    {
      slug: 'media',
      fields: [
        {
          name: 'isShared',
          type: 'checkbox',
          defaultValue: false,
          // you likely want to set access control on fields like this
          // to prevent just any user from modifying it
        },
      ],
      access: {
        read: ({ req, doc }) => {
          if (!req.user) return false

          const whereConstraint = {
            or: [
              {
                isShared: {
                  equals: true,
                },
              },
            ],
          }

          const tenantAccessResult = getTenantAccess({ user: req.user })

          if (tenantAccessResult) {
            whereConstraint.or.push(tenantAccessResult)
          }

          return whereConstraint
        },
      },
    },
  ],
})

Placing the tenants array field

In your users collection you may want to place the field in a tab or in the sidebar, or customize some of the properties on it.

You can use the tenantsArrayField.includeDefaultField: false setting in the plugin config. You will then need to manually add a tenants array field in your users collection.

This field cannot be nested inside a named field, ie a group, named-tab or array. It can be nested inside a row, unnamed-tab, collapsible.

To make it easier, this plugin exports the field for you to import and merge in your own properties.

import type { CollectionConfig } from 'payload'
import { tenantsArrayField } from '@payloadcms/plugin-multi-tenant/fields'

const customTenantsArrayField = tenantsArrayField({
  arrayFieldAccess: {}, // access control for the array field
  tenantFieldAccess: {}, // access control for the tenants field on the array row
  rowFields: [], // additional row fields
})

export const UsersCollection: CollectionConfig = {
  slug: 'users',
  fields: [
    {
      ...customTenantsArrayField,
      label: 'Associated Tenants',
    },
  ],
}

Similar plugins

View on GitHubView on npmOfficial Payload plugin