---
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<Config>({
      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: '(?<tenantDomain>.*)' }],
  }]
}
```

## `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<Config>({
  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
