@zealamic/payload-plugin-rbac
Centralized role-based access control (RBAC) for Payload CMS v3 (payload ^3.84.1). Not compatible with Payload 2.x.

Permissions live in the database (feature + action), are assigned to roles, and enforced via reusable access helpers — editable in Admin without redeploying policy code.
Row-level access (
dataScope): add a field storing the creator’s user id (textis enough;relationshipoptional), set it on create, wiregetPermissionAccess. → UTILS
Documentation
| Guide | Read when you need to… |
|---|---|
| COLLECTIONS | Plugin collections, users augmentation, dataScope, permission matrix, field/access overrides |
| UTILS | getPermissionAccess, data-scope filters, field merge helpers |
| TRANSLATIONS | Admin labels, select options, matrix UI strings (en, vi, …) |
| CUSTOM_COMPONENTS | Custom matrix checkboxes / search input (client field component) |
Typical flow: install → register plugin → seed RBAC data (COLLECTIONS) → add ownership fields + getPermissionAccess on app collections (UTILS) → translate Admin UI (TRANSLATIONS).
Demos in this repo: dev/rbac.ts, dev/collections/posts.ts, dev/components/role-permission-matrix-field.tsx.
Key features
- Five RBAC collections — features, actions, permissions, roles, join table (details)
- Multi-role users — union of enabled grants across assigned roles
- Granular permissions — any
featureCode+actionCodepair (helpers) - Data scope —
own/hierarchy/all; needs ownership field with user id (UTILS) - Auth users slug —
config.admin.user(defaultusers) for hierarchy +options.usersCollectionSlug - Permission matrix — role update UI; syncs draft →
roles-permissionson save - Reorder drawers — drag-and-drop
sortOrderfor permission features and actions on each collection list view - Custom matrix UI — optional client field component + render props (CUSTOM_COMPONENTS)
- TypeScript — typed plugin config and exported helpers/types from the main package
- i18n — plugin translations merged into Payload i18n (guide)
Installation
npm install @zealamic/payload-plugin-rbac
# or: yarn add / pnpm add @zealamic/payload-plugin-rbacQuick start
1. Register the plugin
import { payloadPluginRBAC } from "@zealamic/payload-plugin-rbac";
export default buildConfig({
plugins: [
payloadPluginRBAC({
autoModifyUsersCollection: true, // roles, isSuperAdmin, parent, default user access
// collections: { ... } → https://github.com/zealamic/payload-plugin-rbac/blob/main/docs/COLLECTIONS.md
// translations: { ... } → https://github.com/zealamic/payload-plugin-rbac/blob/main/docs/TRANSLATIONS.md
// components: { rolePermissionMatrixField: "..." } → custom matrix Field (client module)
}),
],
});2. Migration
After adding the plugin to payload.config.ts, run a Payload migration if your database schema is not up to date:
npm run payload migrate:create
# or: yarn payload migrate:create
# or: pnpm payload migrate:createThen apply the migration with migrate (or your project's usual migration workflow).
Bootstrap a super admin: RBAC collections are restricted to super admins by default. Set
isSuperAdmin: trueon at least one user (via seed script, Local API, or direct database update) before you can manage roles, permissions, and the permission matrix in Admin.
3. Seed RBAC data (Admin or script)
- permission-features — e.g.
posts,users(code=featureCodein access helpers); use Reorder on the list view to set row order in the matrix - permission-actions — e.g.
create,read,update,delete(main/subtypes); use Reorder to set main column and sub-action order - permissions — one row per feature + action pair (
status: active) - roles — set dataScope; open update screen, configure matrix → Save
- users — assign roles; bootstrap isSuperAdmin via seed/API
→ Full reference: COLLECTIONS
4. Protect app collections
For dataScope, each collection needs: (1) ownership field with user id — text default, relationship only for Admin UI, (2) create hook (createdByOnCreateBeforeChangeHook), (3) getPermissionAccess with options on read and mode: "modify" on update / delete. Full guide → UTILS · relationship demo: dev/collections/posts.ts
import type { CollectionConfig } from "payload";
import { createdByOnCreateBeforeChangeHook, getPermissionAccess } from "@zealamic/payload-plugin-rbac";
export const Posts: CollectionConfig = {
slug: "posts",
access: {
read: getPermissionAccess({ featureCode: "posts", actionCode: "read", options: {} }),
create: getPermissionAccess({ featureCode: "posts", actionCode: "create" }),
update: getPermissionAccess({ featureCode: "posts", actionCode: "update", mode: "modify" }),
delete: getPermissionAccess({ featureCode: "posts", actionCode: "delete", mode: "modify" }),
},
hooks: { beforeChange: [createdByOnCreateBeforeChangeHook] },
fields: [{ name: "createdBy", type: "text", admin: { readOnly: true } }],
};Access order: no user → deny · super admin → allow · else → matrix permission (+ scope when options / mode: "modify").
Reorder overview (features & actions)
List views for permission-features and permission-actions include a Reorder drawer to set sortOrder (hidden on edit forms). Order drives the role permission matrix: features → rows, main actions → columns, sub actions → checkbox order under each row.

| Step | Action |
|---|---|
| Features | Permission Features → Reorder → drag → Save order |
| Actions | Permission Actions → Reorder → pick Main or Sub → drag → Save order |

API: POST /api/permission-features/reorder · POST /api/permission-actions/reorder — body { "sortedItems": [{ "id": "…", "sortOrder": 0 }] }. Requires update access (super admin by default). Details → COLLECTIONS · labels → TRANSLATIONS — Reorder drawers.
Plugin options
| Option | Default | Description |
|---|---|---|
disabled |
false |
Skip runtime i18n/onInit wiring; collections still register in schema |
autoModifyUsersCollection |
true |
Add RBAC fields, parent-path hooks, and default access on the auth users collection |
translations |
— | Admin + matrix i18n → TRANSLATIONS |
collections |
— | Per-collection fields / access / admin overrides → COLLECTIONS |
components.rolePermissionMatrixField |
default client export | Import-map path to a custom matrix Field component (client module) |
Types import from the main entry:
import type {
RBACTranslations,
PayloadPluginRBACConfig,
} from "@zealamic/payload-plugin-rbac";Exported helpers (summary)
Full reference: UTILS
| Function | Purpose |
|---|---|
getPermissionAccess |
Unified access: permission-only, read Where, or modify per-document |
getCreatedByRelationshipField / createdByOnCreateBeforeChangeHook |
Ownership field + create hook → UTILS |
resolveUsersCollectionSlug |
Same admin.user → slug resolution as the plugin |
getSuperAdminAccess |
Super admin only (default on RBAC collections) |
getAuthenticatedOrSuperAdminAccess |
Owner or super admin |
canAccessDocumentByDataScope |
Low-level single-document RBAC + scope check |
resolveEffectiveDataScope / getHierarchyVisibleUserIds |
Scope resolution |
getDataScopeReadWhere / mergeDataScopeWhere |
Query filters |
getMergedFieldAffectingData / getArrayOfMergedFieldAffectingData |
Field merge for overrides |
Constants: CONSTANTS.ROLE.DATA_SCOPE, CONSTANTS.PERMISSION_ACTION.TYPE, etc.
Package exports
| Import | Contents |
|---|---|
@zealamic/payload-plugin-rbac |
payloadPluginRBAC, utils, constants, TypeScript types |
@zealamic/payload-plugin-rbac/client |
RolePermissionMatrixClient, PermissionActionReorderClient, PermissionFeatureReorderClient, createRolePermissionMatrixClient, and related types |
Images from demo

Reorder screenshots: Reorder overview above.
License
MIT
If this plugin helps your team ship safer access control with less friction, thank you for giving it a place in your stack.