--- title: "Multi-Tenant Plugin" aliases: [multi-tenancy, plugin-multi-tenant, tenant-isolation] tags: [payloadcms, plugins, multi-tenant, access-control] sources: [raw/plugins__multi-tenant.md] created: 2026-05-15 updated: 2026-05-15 --- ## Overview `@payloadcms/plugin-multi-tenant` scaffolds full multi-tenancy inside a single Payload instance. It adds a `tenant` relationship field to every specified collection, injects a tenant selector into the Admin Panel, and filters list views + relationship pickers to the active tenant. ## Core Features - Adds `tenant` field to each opted-in collection - Admin Panel tenant selector — switches context, filters all list views - Filters relationship fields by active tenant - **Global-like collections** — `isGlobal: true` enforces 1 doc per tenant - Auto-assigns tenant to new documents - Cleans up documents when a tenant is deleted (`cleanupAfterTenantDelete`, default `true`) > **Warning:** Tenant deletion cascades to all related documents by default. Apply strong access control on the tenants collection or set `cleanupAfterTenantDelete: false`. ## Installation ```bash pnpm add @payloadcms/plugin-multi-tenant ``` ## Basic Config ```ts import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant' import type { Config } from './payload-types' export default buildConfig({ collections: [ { slug: 'tenants', admin: { useAsTitle: 'name' }, fields: [ { name: 'name', type: 'text', required: true }, { name: 'slug', type: 'text', required: true }, { name: 'domain', type: 'text', required: true }, ], }, ], plugins: [ multiTenantPlugin({ collections: { pages: {}, navigation: { isGlobal: true }, // 1 doc per tenant }, }), ], }) ``` You own the `tenants` collection — add any fields you need (slug, domain, plan, etc.). ## Key Options | Option | Default | Purpose | |--------|---------|---------| | `collections` | required | Map of collection slugs to per-collection options | | `tenantsSlug` | `'tenants'` | Slug for the tenants collection | | `cleanupAfterTenantDelete` | `true` | Remove tenant's docs on deletion | | `userHasAccessToAllTenants` | — | Function for super-admin bypass | | `tenantsArrayField.includeDefaultField` | `true` | Auto-add tenants array to users | | `useBaseFilter` | `true` | Filter list view + relationships by tenant | | `useTenantAccess` | `true` | Apply access constraints per tenant | | `debug` | `false` | Show tenant field in Admin UI | ### Per-Collection Options ```ts collections: { pages: { isGlobal: false, // single doc per tenant? useBaseFilter: true, // filter list + relationships useTenantAccess: true, // enforce tenant access control customTenantField: false, // manually place tenantField export accessResultOverride: ..., // override access control result } } ``` ## Frontend Querying Filter any enabled collection by tenant field: ```tsx const pages = await payload.find({ collection: 'pages', where: { 'tenant.slug': { equals: 'gold' }, }, overrideAccess: false, }) ``` ### Next.js Domain-Based Rewrites Route per domain to the matching tenant: ```ts async rewrites() { return [{ source: '/((?!admin|api)):path*', destination: '/:tenantDomain/:path*', has: [{ type: 'host', value: '(?.*)' }], }] } ``` ## `useTenantSelection` Hook Access current tenant context in custom Admin components: ```tsx import { useTenantSelection } from '@payloadcms/plugin-multi-tenant/client' const { selectedTenantID, setTenant, options } = useTenantSelection() setTenant({ id: 'abc123', refresh: true }) ``` ## Super-Admin Bypass ```ts multiTenantPlugin({ collections: { pages: {} }, userHasAccessToAllTenants: (user) => user.roles?.includes('super-admin'), }) ``` ## Key Takeaways - **You own the tenants collection** — plugin just references it; define your own fields (slug, domain, plan, etc.) - **`isGlobal: true`** enforces exactly one document per tenant (navigation, settings, etc.) - **Cascade delete is on by default** — guard the tenants collection or disable with `cleanupAfterTenantDelete: false` - **`useBaseFilter`** filters both the list view AND relationship pickers — setting to `false` requires manual filtering - **`userHasAccessToAllTenants`** is the correct hook for super-admin users, not disabling `useTenantAccess` - **`useTenantSelection`** hook lets custom Admin components read/set the active tenant - **Domain routing**: combine with Next.js rewrites to map domains → tenant slugs automatically ## Related - [[wiki/payloadcms/plugins|Official Plugins]] — all 10 official plugins overview - [[wiki/payloadcms/access-control|Access Control]] — how access control integrates with tenant isolation - [[wiki/payloadcms/collection-config|Collection Config]] — collection-level options used by the plugin - [[wiki/payloadcms/authentication-overview|Authentication Overview]] — user collection that gets the tenants array field ## Sources - `raw/plugins__multi-tenant.md` - https://payloadcms.com/docs/plugins/multi-tenant