đȘ Mastering Roles and Multi-Tenancy in Payload CMS
Date Published

đ The Problem with Old CMS Access Control (aka "The Click Hell Era")
Letâs be real for a second:
If youâve ever tried to set up roles or multi-tenancy in legacy CMSs, you probably ended up with either:
- A spaghetti bowl of âEditorâ, âSuperEditorâ, âMegaEditorâ, and âWeForgotWhatThisDoesEditorâ roles
- Or worse â hacking around permission settings with checkboxes buried under four tabs and an âAdvanced Settingsâ dropdown nobody dared to open

âš Why Payloadâs Access Control Is Different (And Better)
Payload CMS doesn't treat access control as an afterthought. It's function-based, not checkbox-based. This means instead of ticking random boxes in a UI, you just write functions â clean, readable functions that make sense to developers.
Youâre not stuck with âread-onlyâ vs âadminâ â you can define anything from:
- Role-based access (classic)
- Field-level permissions
- Per-document conditions
- Organization-based multi-tenancy
- Even: "Users on a Pro plan can create posts, but Free users can only like them" đ€Ż
đ§âđ» Defining User Roles Like a Pro
Hereâs a minimal setup for defining roles inside your payload.config.ts. You can start with simple roles like admin, editor, and viewer, and build from there.
1// In your payload.config.ts2export default buildConfig({3 collections: [4 {5 slug: 'users',6 auth: true,7 fields: [8 {9 name: 'role',10 type: 'select',11 options: [12 { label: 'Admin', value: 'admin' },13 { label: 'Editor', value: 'editor' },14 { label: 'Viewer', value: 'viewer' },15 ],16 defaultValue: 'viewer',17 required: true,18 },19 // Other user fields20 ],21 },22 // Other collections23 ],24});25
Now that youâve got roles, youâre in full control of who can do what across your CMS.

đ§ Collection-Level and Field-Level Access Control
Payload lets you be extremely specific about access. Hereâs what that can look like for a blog post collection:
1// Collection with granular access control2{3 slug: 'posts',4 access: {5 create: ({ req }) => req.user?.role === 'admin' || req.user?.role === 'editor',6 read: ({ req }) => true, // Anyone can read posts7 update: ({ req, doc }) => {8 // Admins can update any post9 // Editors can only update their own posts10 return req.user?.role === 'admin' ||11 (req.user?.role === 'editor' && req.user.id === doc.author);12 },13 delete: ({ req }) => req.user?.role === 'admin', // Only admins can delete14 },15 fields: [16 {17 name: 'title',18 type: 'text',19 access: {20 // All authenticated users can read the title21 // Only admins and editors can update it22 update: ({ req }) => req.user?.role === 'admin' || req.user?.role === 'editor',23 },24 },25 // Other fields26 ],27}28
You can control who can edit what, all the way down to individual fields â because hey, maybe editors can update the title, but not the secret SEO strategy field đ

đą Multi-Tenancy: One Payload App, Many Organizations
Now this is where things get really exciting.
Multi-tenancy with Payload means you can have multiple organizations using the same CMS instance, each with isolated content and users.
Hereâs how it works:
- Organizations have members
- Users belong to organizations
- Content (like projects or posts) belongs to an organization
- Access control functions check org membership before showing content
Example: Showing projects only from the userâs org:
1// Organizations collection2{3 slug: 'organizations',4 fields: [5 {6 name: 'name',7 type: 'text',8 },9 {10 name: 'members',11 type: 'relationship',12 relationTo: 'users',13 hasMany: true,14 },15 ],16}1718// Users with organization relationship19{20 slug: 'users',21 auth: true,22 fields: [23 {24 name: 'organizations',25 type: 'relationship',26 relationTo: 'organizations',27 hasMany: true,28 },29 // Other fields30 ],31}3233// Content with organization context34{35 slug: 'projects',36 access: {37 read: async ({ req }) => {38 if (!req.user) return false;3940 // If admin, return all projects41 if (req.user.role === 'admin') return true;4243 // Otherwise, only return projects from the user's organizations44 return {45 organization: {46 in: req.user.organizations,47 },48 };49 },50 // Similar patterns for create, update, delete51 },52 fields: [53 {54 name: 'organization',55 type: 'relationship',56 relationTo: 'organizations',57 required: true,58 },59 // Project fields60 ],61}62
Thatâs it. You now have true multi-tenancy. No tenant IDs in the URL, no hard-to-maintain API filters. Just functions.

đ„ Organization Management â Like You Actually Meant It
Payload lets you build proper organization features, like:
- Invite flows for members
- Assigning different roles within the org
- Approval workflows
- Custom content access based on tiers/subscriptions
And because itâs all code, everything is version-controlled and testable. No more guessing what a role does in production đ«Ł
đ Conclusion: Function-Based Access Control FTW
After dealing with rigid, click-heavy CMSs, Payload feels like a breath of fresh air.
- Roles? â Easy and extendable
- Multi-tenancy? â Built right in
- Access control down to the field level? â You got it
- Peace of mind as a developer? â Priceless
Whether you're building an internal dashboard, a multi-client platform, or just a highly-permissioned content site â Payload makes you feel like youâre building software the way it shouldâve always worked.
Like what you read? Here are some related posts:

The Fullstack Framework Next.js Devs Have Been Waiting For