Netlify CMS (now Decap CMS) doesn't have built-in role management like WordPress or traditional CMSs. Instead, it relies on Git provider permissions and editorial workflow configuration. This guide explains how to implement role-based access control.
Understanding Netlify CMS Permission Model
Git-Based Permissions
Netlify CMS inherits permissions from your Git provider:
Repository Permission = CMS Permission
| Git Provider | Permission Level | CMS Access | Can Edit | Can Publish |
|---|---|---|---|---|
| GitHub | Read | View only | No | No |
| GitHub | Write | Full access | Yes | Via PR |
| GitHub | Admin | Full access | Yes | Yes (direct merge) |
| GitLab | Guest | No access | No | No |
| GitLab | Reporter | View only | No | No |
| GitLab | Developer | Full access | Yes | Via MR |
| GitLab | Maintainer | Full access | Yes | Yes (direct merge) |
| Bitbucket | Read | View only | No | No |
| Bitbucket | Write | Full access | Yes | Via PR |
| Bitbucket | Admin | Full access | Yes | Yes (direct merge) |
No Database-Level Roles
Unlike WordPress (Admin, Editor, Author, Contributor), Netlify CMS has no user database:
WordPress Approach:
Admin > Editor > Author > Contributor > Subscriber
Netlify CMS Approach:
Repository permissions + Editorial workflow + Branch protection
Implementing Role-Based Access
Editorial Workflow for Access Control
Enable editorial workflow to create a review process:
# static/admin/config.yml
backend:
name: github # or gitlab, bitbucket
repo: your-username/your-repo
branch: main
publish_mode: editorial_workflow
# Optional settings
media_folder: "static/uploads"
public_folder: "/uploads"
collections:
- name: "blog"
label: "Blog"
folder: "content/blog"
create: true
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "Body", name: "body", widget: "markdown"}
Workflow Stages
1. Draft
- Who: Anyone with Write/Developer permission
- What: Create content, save drafts
- Git: No commit yet (stored in browser localStorage or as branch)
2. In Review
- Who: Content creators move to review
- What: Creates pull/merge request
- Git: Creates feature branch, opens PR/MR
3. Ready
- Who: Reviewers (users with merge permission)
- What: Approve content for publishing
- Git: Approval recorded on PR/MR
4. Published
- Who: Users with merge permission
- What: Merge to main branch
- Git: PR/MR merged, triggers build
Role Patterns
Pattern 1: Content Creator Role
Goal: Users can create/edit but not publish directly.
Implementation:
1. GitHub Configuration:
Repository → Settings → Branches → Branch protection rules
Branch: main
☑ Require pull request reviews before merging
Required approvals: 1
☑ Restrict who can push to matching branches
(Don't include content creators)
2. Grant Write Access:
Repository → Settings → Collaborators → Add people
Permissions: Write
3. Editorial Workflow:
# config.yml
publish_mode: editorial_workflow
Result:
- Content creators can edit via CMS
- Changes create PRs automatically
- Cannot merge (publish) directly
- Requires reviewer approval
Pattern 2: Reviewer Role
Goal: Users can review and approve content.
Implementation:
1. GitHub Configuration:
Settings → Branches → Branch protection
☑ Require pull request reviews before merging
Required approvals: 1
Reviewers:
- Add specific users or teams
2. Grant Write or Admin Access:
Collaborators → Add reviewer
Permissions: Write (can approve) or Admin (can approve and merge)
3. Review Process:
- Content creator submits to "In Review"
- PR created automatically
- Reviewer sees notification
- Reviewer approves via GitHub/CMS
- Reviewer or admin merges to publish
Pattern 3: Publisher Role
Goal: Users can publish content directly without review.
Implementation:
1. Grant Admin Permission:
Repository → Collaborators → Add people
Permissions: Admin
2. No Branch Protection (or exempt admins):
Settings → Branches → Branch protection
☑ Include administrators
(Unchecked - admins can bypass protection)
3. Direct Publish:
# config.yml
# No editorial_workflow - direct commits to main
publish_mode: simple # or omit, defaults to simple
Result:
- Publishers can commit directly to main
- No PR required
- Immediate publication
Pattern 4: Read-Only Role
Goal: Users can view content in CMS but not edit.
Implementation:
1. GitHub Read Permission:
Collaborators → Add people
Permissions: Read
2. CMS Behavior:
- User can log in to CMS
- Can browse content
- Cannot edit, save, or publish
- "Edit" button disabled
Note: This is limited functionality. Most read-only users don't need CMS access.
Collection-Level Permissions
Restrict Collections by User
While Netlify CMS doesn't have built-in user-based collection filtering, you can implement it:
Option 1: Separate Repositories
blog-repo → Blog editors access this
docs-repo → Documentation team access this
Option 2: Git Branches
# config.yml for blog editors
backend:
name: github
repo: your-org/content
branch: blog # Only edit blog branch
# config.yml for docs team
backend:
name: github
repo: your-org/content
branch: docs # Only edit docs branch
Option 3: Custom CMS Builds
Build separate CMS instances with different config.yml:
/admin/blog/config.yml → Blog collections only
/admin/docs/config.yml → Docs collections only
Deploy at different URLs:
yoursite.com/admin/blog→ Blog teamyoursite.com/admin/docs→ Docs team
Advanced Permission Patterns
GitHub Teams
Manage groups of users:
1. Create Team:
Organization → Teams → New team
Team name: "Content Editors"
2. Add Team to Repository:
Repository → Settings → Collaborators and teams
Add team: "Content Editors"
Permissions: Write
3. Branch Protection with Teams:
Settings → Branches → Branch protection
Required approvals: 1
Restrict who can dismiss reviews:
Teams: "Senior Editors"
Benefits:
- Manage multiple users at once
- Consistent permissions across repos
- Easy onboarding/offboarding
GitLab Groups
Similar to GitHub teams:
1. Create Group:
GitLab → Groups → New group
2. Add Members to Group:
Group → Members → Invite
Role: Developer, Maintainer, etc.
3. Add Group to Project:
Project → Members → Invite group
Role: Developer
CODEOWNERS File
Define who must review specific files:
# .github/CODEOWNERS
# Blog posts require blog-team approval
/content/blog/** @your-org/blog-team
# Documentation requires docs-team approval
/content/docs/** @your-org/docs-team
# Site config requires admin approval
/static/admin/** @your-org/admins
Enforcement:
Settings → Branches → Branch protection
☑ Require review from Code Owners
Custom Authentication
Implementing Custom Roles
For advanced role management beyond Git permissions:
Option 1: Netlify Identity with Metadata
# config.yml
backend:
name: git-gateway
Add user metadata via Netlify API:
// Add role metadata to user
const response = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/identity/users/${userId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
app_metadata: {
role: 'editor' // or 'admin', 'reviewer'
}
})
});
Read role in CMS:
// custom-cms-widget.js
const user = netlifyIdentity.currentUser();
const role = user.app_metadata.role;
if (role === 'editor') {
// Show only specific collections
}
Option 2: Custom Backend
Build custom authentication backend:
# config.yml
backend:
name: custom
url: https://your-api.com/netlify-cms-auth
Implement custom backend that:
- Authenticates users
- Returns user roles
- Filters collections based on role
- Controls publish permissions
Permission Troubleshooting
User Can't Edit
Symptom: User logs in but sees read-only interface.
Checklist:
- User has Write/Developer permission (not Read/Reporter)
- Repository permissions synced (may take a few minutes)
- User authenticated with correct account
- CMS config points to correct repository
Fix:
- Verify user's Git provider permission
- Increase to Write/Developer
- User logs out and logs back in to CMS
User Can Edit But Can't Publish
Symptom: User can save drafts but publish fails.
Causes:
- Branch protection enabled
- User lacks merge permission
- Editorial workflow misconfigured
Fix:
If editorial workflow enabled:
- Expected behavior - user should submit for review
- Reviewer with merge permission must approve
If direct publish desired:
- Grant user Admin permission
- Or exempt from branch protection
Changes Not Appearing
Symptom: User publishes but changes don't appear on site.
Checklist:
- PR/MR actually merged
- Build triggered after merge
- Build succeeded (check Netlify deploy log)
- Cache cleared
Fix:
- Check Netlify → Deploys
- Verify merge happened in Git provider
- Trigger manual deploy if needed
Best Practices
1. Start with Editorial Workflow
Always recommend:
publish_mode: editorial_workflow
Benefits:
- Preview changes before publish
- Peer review process
- Git history of approvals
- Easy rollback
2. Protect Main Branch
Minimum protection:
☑ Require pull request reviews
☑ Require status checks to pass
3. Use Teams/Groups
Instead of:
Individual users: alice, bob, carol, david...
Use:
Teams: content-editors, reviewers, admins
4. Audit Regularly
Monthly review:
- Active users
- Permission levels
- Unused accounts
- Team memberships
5. Document Your Workflow
Create internal documentation:
# Content Publishing Workflow
## Roles
**Content Creators** (Write permission)
- Create drafts
- Edit content
- Submit for review
**Reviewers** (Write permission + Code Owner)
- Review submissions
- Request changes
- Approve content
**Publishers** (Admin permission)
- Merge approved content
- Emergency hotfixes
- Configuration changes
Next Steps
- Add/Remove Users - Manage team members
- Configure Editorial Workflow - Set up review process