Skip to content

Role-Based Access

Add an access key to any tab, section, or field to gate it. Two verbs — view and edit — accept either a role slug (editor, author) or a capability (manage_options, edit_posts). Arrays evaluate as OR.

// Split view and edit per verb.
[
'id' => 'api_key',
'type' => 'text',
'label' => __('API Key', 'my-plugin'),
'access' => [
'view' => 'editor', // editors and above can see the key
'edit' => 'manage_options', // only admins can change it
],
],
// Shorthand — same role/cap for both verbs.
[
'id' => 'internal_note',
'type' => 'textarea',
'label' => __('Internal note', 'my-plugin'),
'access' => 'editor',
],
// Hide an entire section from non-admins.
[
'id' => 'danger_zone',
'title' => __('Danger Zone', 'my-plugin'),
'access' => ['view' => 'manage_options'],
'fields' => [ /* … */ ],
],

When the framework sees a string in access, it tries role slugs first via get_role(), then falls back to capability lookups via current_user_can(). This means 'editor' works as a role and 'edit_posts' works as a capability without you having to declare which is which.

Arrays mean any of these grants access:

'access' => ['view' => ['editor', 'author', 'edit_others_posts']],

Section and tab access bubbles up from fields

Section titled “Section and tab access bubbles up from fields”

A tab or section with no access key of its own takes its visibility from its children: it shows for anyone who can view at least one field inside it, and hides when they can view none. Annotate the fields and the containers follow — no need to repeat the rule on the section to stop it rendering as an empty card.

Give a tab or section its own access key only when you want to gate it top-down regardless of its fields. wp-wireframe/access/resolve still runs for containers without an access key, so a filter can deny them.

Once at least one access key is declared anywhere on the page, RBAC mode engages.

BehaviourDefaultOverride
Page capabilitymanage_optionsSet capability on the boot config
Menu visibilityFloor drops to read; menu suppressed entirely when the user has zero accessible elementsn/a
access shorthand 'editor'Applied to both view and editUse long form ['view' => …, 'edit' => …] to split
access arraysLogical OR — any match grants accessn/a
edit without viewImplicitly deniededit always requires view
Field with no access keyInherits from its section / tab (ultimately the page capability)Add an access key
Tab / section with no access keyVisibility bubbles up — shown if the user can view at least one descendant fieldAdd an explicit access key to gate it top-down
Non-viewable elementsStripped from the config sent to the browser entirelywp-wireframe/config/for_user filter
Non-editable but viewable fieldsRendered with the input disabled; server rejects any writesn/a
Empty tab/section after filteringAuto-hiddenn/a
Save endpointMerges editable fields into existing values — fields outside scope are never touchedwp-wireframe/save/editable_fields, wp-wireframe/save/payload
Reset endpointResets only the user’s editable fieldswp-wireframe/access/can_reset filter
Action / header action $valuesOnly fields the user can view; view-only fields carry the stored value (or default), never the posted onen/a
Save / Reset buttonsHidden when the user has no editable fieldsn/a
REST permission callbackread AND: GET — any viewable element; POST — an editable field; DELETE — an editable field and can_reset. Action and table routes use the GET rulewp-wireframe/access/resolve filter

Five filters let you override decisions per-request without forking the config tree:

// Override the yes/no for a single (verb, level, id, user) check.
add_filter('wp-wireframe/access/resolve', fn($allowed, $ctx) => $allowed, 10, 2);
// Mutate the config after access filtering, before localize.
add_filter('wp-wireframe/config/for_user', fn($config, $pageId, $map) => $config, 10, 3);
// Prune or extend the writable field list for the current save.
add_filter('wp-wireframe/save/editable_fields', fn($ids, $pageId, $map) => $ids, 10, 3);
// Transform sanitised values before they're merged into saved state.
add_filter('wp-wireframe/save/payload', fn($values, $pageId, $payload) => $values, 10, 3);
// Override whether the current user may reset on this page.
add_filter('wp-wireframe/access/can_reset', fn($can, $user, $viewable, $editable) => $can, 10, 4);

See Hooks & Filters for the full signatures.

When the current user has view but not edit access, the field’s React component receives a field.readOnly prop. Honour it by disabling your input — the server enforces writes regardless, so this is a UI hint not a security boundary.

function MyFieldEdit({ field, value, onChange }) {
return (
<input
value={value}
onChange={(e) => onChange(e.target.value)}
disabled={field.readOnly}
/>
);
}