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): list collection slugs intargetCollectionsfor auto ownership field + create hook, then wiregetPermissionAccess. → COLLECTIONS — targetCollections · manual setup → UTILS
| 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 with targetCollections (COLLECTIONS) → run migration → seed RBAC data (COLLECTIONS) → add 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.
- 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; usetargetCollectionsfor ownership field + hook, or add manually (UTILS) targetCollections— auto-add hidden ownership field + create hook on app collections (COLLECTIONS)- 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)
npm install @zealamic/payload-plugin-rbac
# or: yarn add / pnpm add @zealamic/payload-plugin-rbacimport { payloadPluginRBAC } from "@zealamic/payload-plugin-rbac";
export default buildConfig({
plugins: [
payloadPluginRBAC({
autoModifyUsersCollection: true, // roles, isSuperAdmin, parent, default user access
targetCollections: ["posts"], // ownership field + create hook — see step 4
// 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)
}),
],
});After adding the plugin (and especially after changing targetCollections), update your database schema:
yarn payload migrate:create
yarn payload migrate(npm run / pnpm equivalents work too.)
Run migrate:create when new fields are added to collections (e.g. first time you list a slug in targetCollections). Then migrate to apply pending migrations.
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.
- 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
Recommended: list slugs in targetCollections — the plugin adds a hidden ownership field (createdBy text by default) and a create hook. You only wire getPermissionAccess on the collection. Full options → COLLECTIONS — targetCollections.
// payload.config.ts
payloadPluginRBAC({
targetCollections: ["posts"],
});
// collections/posts.ts — no manual createdBy field or hook needed
import type { CollectionConfig } from "payload";
import { 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" }),
},
fields: [{ name: "title", type: "text", required: true }],
};After adding or changing targetCollections, run yarn payload migrate:create then yarn payload migrate.
Manual setup (visible fields, custom layout, or full control): add ownership field + create hook yourself → UTILS — Ownership field · relationship demo: dev/collections/posts.ts
Access order: no user → deny · super admin → allow · else → matrix permission (+ scope when options / mode: "modify").
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.
| 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 |
targetCollections |
— | Auto ownership field + create hook on listed app collections → COLLECTIONS |
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,
TargetCollection,
} from "@zealamic/payload-plugin-rbac";Full reference: UTILS
| Function | Purpose |
|---|---|
getPermissionAccess |
Unified access: permission-only, read Where, or modify per-document |
targetCollections (plugin config) |
Quick ownership field + hook — COLLECTIONS |
getCreatedByRelationshipField / createCreatedByOnCreateBeforeChangeHook |
Manual ownership field + 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.
| Import | Contents |
|---|---|
@zealamic/payload-plugin-rbac |
payloadPluginRBAC, utils, constants, TypeScript types |
@zealamic/payload-plugin-rbac/client |
RolePermissionMatrixClient, PermissionActionReorderClient, PermissionFeatureReorderClient, createRolePermissionMatrixClient, and related types |
Reorder screenshots: Reorder overview above.
MIT
If this plugin helps your team ship safer access control with less friction, thank you for giving it a place in your stack.




