Multi-Tenant Plugin
This plugin sets up multi-tenancy for your application from within your Admin Panel. It does so by adding a tenant field to all specified collections. Your front-end application can then query data by tenant. You must add the Tenants collection so you control what fields are available for each tenant.
Core features
- Adds a
tenantfield to each specified collection - Adds a tenant selector to the admin panel, allowing you to switch between tenants
- Filters list view results by selected tenant
- Filters relationship fields by selected tenant
- Ability to create "global" like collections, 1 doc per tenant
- Automatically assign a tenant to new documents
- Rejects writes that assign a tenant the user is not a member of
Installation
Install the plugin using any JavaScript package manager like pnpm, npm, or Yarn:
Options
The plugin accepts an object with the following properties:
Basic Usage
In the plugins array of your Payload Config, call the plugin with options:
Tenant write enforcement
Every create and update to a tenant-enabled collection is checked. If the request assigns a tenant that the user is not a member of, the write is rejected with a validation error on the tenant field.
This applies to the tenant a document starts in and the tenant it moves to. A user cannot create a new document in another tenant or move an existing document to another tenant.
The check runs on drafts and autosave saves as well as ordinary saves. Payload skips field validation for those operations, so the plugin also enforces membership in a collection beforeValidate hook.
What is checked
The tenant is compared with the tenants assigned to the user on the admin users collection. The check is skipped when:
- The user passes your
userHasAccessToAllTenantsfunction - The user belongs to a different auth collection. These users do not have a tenants array, which matches how the plugin's access control treats them
- The tenant is unchanged. An update that does not change the tenant field is not blocked by this check
- The call comes from the Local API with no user, such as a seed script or a migration
All other writes are checked, including requests with no user. An unauthenticated request belongs to no tenant, so it cannot assign one. If a collection allows public creates and must set a tenant, do this in server-side code through the Local API instead of the request body.
Other auth collections
Users from an auth collection other than the tenant-managed collection cannot write the tenant field. Attempts to set or change the tenant are ignored. These users can still update other fields if the collection access rules allow it. This applies when the plugin adds the field, or when a customTenantField configuration passes adminUsersSlug to tenantField.
filterOptions and write permission
filterOptions on the tenant field controls which tenants the admin panel shows. It does not control which tenants the server accepts on write.
If you override filterOptions to show more tenants, the additional tenants are still rejected on save. Use userHasAccessToAllTenants to let a user write outside their assigned tenants.
The write check reads the user's tenant assignments instead of running the filterOptions query. This avoids a database read in the same transaction as the write.
Collections that place the tenant field themselves
Collections that use customTenantField receive the same collection-hook check. The field checks the user's explicit tenant assignments. If users depend on userHasAccessToAllTenants, pass that function to tenantField when you place it. The collection hook applies the check in all cases.
Access control also applies. A user who cannot update a document is rejected before the tenant is checked.
Front end usage
The plugin scaffolds out everything you will need to separate data by tenant. You can use the tenant field to filter data from enabled collections in your front-end application.
In your frontend you can query and constrain data by tenant with the following:
NextJS rewrites
Using NextJS rewrites and this route structure /[tenantDomain]/[slug], we can rewrite routes specifically for domains requested:
React Hooks
Below are the hooks exported from the plugin that you can import into your own custom components to consume.
useTenantSelection
You can import this like so:
The hook returns the following context:
Examples
The Examples Directory also contains an official Multi-Tenant example.
Was this page helpful?