Permission Matrix

Roles against permissions, in a grid. Inherited grants are drawn differently from direct ones, because "why can this role still delete things" is the question a flat list of ticks cannot answer.

src/components/ui/permission-matrix.tsx

About

From the component's own source, where it was written.

Roles against permissions, as a grid.

Inherited permissions render differently from directly granted ones. A role that gets billing.read because it implies billing.write is not the same as one granted it explicitly, and an admin who cannot tell them apart will revoke the wrong thing and be surprised when access remains.

Locked cells are shown, not hidden. A permission an admin cannot change still needs to be visible, or they will assume it is off and grant it elsewhere.

Install

npx astralyx-ui add permission-matrix

Usage

import { PermissionMatrix } from '@/components/ui/permission-matrix'

<PermissionMatrix permissions={permissions} roles={roles} onToggle={toggle} />

Composer

Change the props and the source below follows.

PermissionOwnerlockedAdminMemberViewer
Records
View records
Edit records
Delete recordsPermanent after 30 days.
Export data
Team
Invite members
Manage roles
Account
Manage billing
Read audit log
Props
tsx
<PermissionMatrix permissions={permissions} roles={roles} onToggle={toggle} />

Examples

Roles against permissions

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

PermissionMatrix

Extends Omit<ComponentProps<'div'>, 'onToggle'>.

PropTypeDefaultDescription
permissionsPermission[]required{ id, label, group?, description? }. Groups become labelled row sections.
rolesPermissionRole[]required{ id, label, granted, inherited?, locked?, lockedPermissions? } — one column each.
onToggle(roleId: string, permissionId: string, granted: boolean) => void—Omit for a read-only matrix, which is the right thing for a permissions reference page.
permissionHeaderReactNode'Permission'Heading over the permission column.
lockedLabelReactNode'locked'Note under a role that cannot be edited.
inheritedLabelstring'Inherited'—
inheritedTitlestring'Inherited from another permission'Tooltip on an inherited grant.
lockedPermissionTitlestring—Tooltip on a cell locked by lockedPermissions, saying why it cannot be granted.
grantedLabelstring'Granted'—
notGrantedLabelstring'Not granted'—

Types

Permission

Roles against permissions, as a grid.

FieldTypeDescription
idstring—
labelReactNode—
group?string—
description?ReactNode—

PermissionRole

FieldTypeDescription
idstring—
labelReactNode—
grantedstring[]Directly granted permission ids.
inherited?string[]Held via implication. Rendered distinctly and not directly revocable, because revoking it here would silently do nothing.
locked?booleanFor a role that cannot be edited at all — an Owner that must keep every permission.
lockedPermissions?string[]Permissions one role can never hold. Those cells are read-only while the rest of the column stays editable — a Viewer that must not be handed billing.