Skip to Content
For DevelopersPermissions & publish gates

Permissions & publish gates

Two related features: roles limit what a user can do inside the editor; publish gates validate the document before it goes live.

Roles

Pass role to <Blok>:

<Blok config={config} role={currentUser.role} />

role is one of "editor" (default), "reviewer", or "viewer".

Role matrix

Capabilityeditorreviewerviewer
View content
Edit fields
Add / remove blocks
Comment
Save drafts
Publish
Versions — save / restore

Import ROLE_MATRIX from @useblok/core if you want to read or check the matrix programmatically:

import { ROLE_MATRIX, useCan, useRole } from "@useblok/core"; function Toolbar() { const canPublish = useCan("publish"); const role = useRole(); return <div>You are {role}. {canPublish ? "✅" : "👀"}</div>; }

<IfCan> helper

Gate a UI branch on a permission:

import { IfCan } from "@useblok/core"; <IfCan action="publish"> <button>Publish now</button> </IfCan>

UI-side permissions are a UX guardrail, not a security boundary. Always re-check permissions in your save / publish API handlers — never trust the role prop alone.

Publish gates

A publish gate is a check that runs before onPublish fires. If any gate returns an error-level issue, publishing is blocked and the user sees a list of problems.

Pass a publishGates config to <Blok>:

<Blok config={config} publishGates={{ // Built-in: SEO readiness always runs. // Set `comments: true` to also require all threads resolved. comments: true, // Custom gate — runs on every publish attempt. custom: async (ctx) => { const issues = []; if (!ctx.data.root.props?.title) { issues.push({ id: "missing-title", severity: "error", message: "Page title is required before publishing.", }); } return issues; }, }} onPublish={handlePublish} />

Issue shape

interface GateIssue { id: string; severity: "error" | "warning"; message: string; /** Optional: block ID to highlight + jump to. */ blockId?: string; /** Optional: field name inside that block. */ fieldName?: string; /** Optional: one-click fix. */ fix?: () => void; }

Gate context

Your custom gate receives:

interface GateContext { data: Data; config: Config; }

Built-in gates

GateEnabled byWhat it checks
SEOalways onMeta title & description, heading hierarchy, image alt text.
CommentspublishGates.comments = trueEvery comment thread is resolved.
Customyour functionAnything you return from publishGates.custom.

Reading the report elsewhere

If you want to show readiness somewhere else in the UI (dashboard, header, etc.), use usePublishGates:

import { usePublishGates } from "@useblok/core"; function ReadinessBadge() { const { issues, hasErrors } = usePublishGates(); return hasErrors ? ( <span>⚠️ {issues.length} issues</span> ) : ( <span>✅ Ready to publish</span> ); }

Server-side enforcement

Repeat after me: the editor’s role gates are UI only. Every server-side write path (save, publish, schedule, unpublish, versions, comments) must check permissions on its own. Treat Blok’s role prop as hint-level — use it to hide buttons, not to protect data.

Last updated on