@multisystemsuite/react-permission-kit
v0.1.7
Published
Enterprise-grade React Role & Permission library for React 16–19. RBAC, wildcards, feature flags, SSR, and router-agnostic guards.
Maintainers
Readme
@multisystemsuite/react-permission-kit
Enterprise-grade React Role & Permission library for React 16, 17, 18, and 19.
Framework-agnostic. Works with React Router v5/v6/v7, Next.js, Vite, CRA, Remix, and SSR.
Table of Contents
Features
- RBAC and permission-based access control
- Multiple roles and permissions
- Wildcard permissions (
users.*,admin.*) - Hierarchical permissions (
inventory.stock.read) - Tenant, organization, and department scoping
- Feature flags (
feature.chat,feature.ai) - Super admin bypass
- Permission groups
- Dynamic permission updates
- Async permission loading
- JWT integration (client-side decode)
- localStorage, sessionStorage, memory, and custom storage adapters
- SSR, StrictMode, and Suspense compatible
- Zero runtime dependencies
- Fully typed TypeScript API
- Tree-shakeable ESM + CommonJS builds
Installation
npm install @multisystemsuite/react-permission-kit
# or
yarn add @multisystemsuite/react-permission-kit
# or
pnpm add @multisystemsuite/react-permission-kitPeer dependencies: react and react-dom (>=16.8.0)
How to Use
Step 1: Wrap your app with PermissionProvider
Always wrap your app (or layout) with PermissionProvider and pass user permissions/roles:
import { PermissionProvider } from '@multisystemsuite/react-permission-kit';
export default function App() {
return (
<PermissionProvider
permissions={['users.read', 'users.create', 'dashboard.view']}
roles={['editor']}
tenant="company-a"
storage="memory" // use "memory" for SSR
>
<YourApp />
</PermissionProvider>
);
}Load permissions from API or JWT:
<PermissionProvider
jwt={accessToken}
onLoad={async () => {
const res = await fetch('/api/me/permissions');
return res.json(); // { permissions: [], roles: [] }
}}
storage="localStorage"
>
<YourApp />
</PermissionProvider>Step 2: Check permissions
Option A — usePermission() hook (best for logic):
import { usePermission } from '@multisystemsuite/react-permission-kit';
function UserPage() {
const { can, cannot, hasRole, hasFeature, isSuperAdmin } = usePermission();
if (!can('users.read')) return <p>Access denied</p>;
return (
<div>
{can('users.create') && <button>Create</button>}
{hasRole('admin') && <AdminPanel />}
{hasFeature('feature.chat') && <ChatWidget />}
{isSuperAdmin() && <SuperAdminTools />}
</div>
);
}Option B — <Can> component (best for JSX):
import { Can, Cannot } from '@multisystemsuite/react-permission-kit';
<Can permission="users.read">
<UserList />
</Can>
<Cannot permission="users.read">
<p>You need users.read permission</p>
</Cannot>Option C — <Can> with modes:
{/* hide (default) | disable | readonly */}
<Can permission="users.edit" disable fallback={<span>Read only</span>}>
<input defaultValue="John" />
</Can>
{/* multiple permissions — any (OR) or all (AND) */}
<Can permissions={['users.read', 'users.export']} mode="all">
<ExportButton />
</Can>Step 3: Conditional UI components
import {
PermissionButton,
PermissionLink,
PermissionRender,
PermissionCard,
PermissionImage,
PermissionBadge,
} from '@multisystemsuite/react-permission-kit';
{/* Button — hide | disable | readonly + tooltip + loading */}
<PermissionButton
permission="users.create"
hide
tooltip="No create access"
isLoading={saving}
onClick={handleCreate}
>
Create User
</PermissionButton>
{/* Link */}
<PermissionLink permission="reports.view" to="/reports">
View Reports
</PermissionLink>
{/* Custom render */}
<PermissionRender
permission="dashboard.view"
feature="feature.analytics"
render={(allowed) => (allowed ? <Dashboard /> : <UpgradeBanner />)}
/>
{/* Card with header, actions, menu */}
<PermissionCard
permission="dashboard.analytics"
title="Analytics"
actions={[{ key: 'export', label: 'Export', permission: 'reports.export' }]}
>
Chart content
</PermissionCard>
{/* Image — hide, blur, or replace */}
<PermissionImage
permission="media.view"
src="/photo.jpg"
blur
replaceWith={<span>No access</span>}
/>
{/* Badge */}
<PermissionBadge role="admin" variant="role" />
<PermissionBadge permission="users.read" variant="permission" />Step 4: Permission Table
Auto-hides unauthorized columns and row actions. Supports pagination, sorting, filtering, selection, bulk actions, and expandable rows.
import {
PermissionTable,
definePermissionActions,
} from '@multisystemsuite/react-permission-kit';
const employees = [
{ id: '1', name: 'Alice', salary: 95000 },
{ id: '2', name: 'Bob', salary: 82000 },
];
function EmployeeTable() {
return (
<PermissionTable
permission="employees.view" // hide entire table if denied
data={employees}
rowKey="id"
columns={[
{ key: 'name', title: 'Name', sortable: true },
{
key: 'salary',
title: 'Salary',
permission: 'employee.salary.view', // column auto-hidden
},
]}
actions={definePermissionActions([
{ key: 'view', label: 'View', permission: 'employee.read' },
{ key: 'edit', label: 'Edit', permission: 'employee.update' },
{ key: 'delete', label: 'Delete', permission: 'employee.delete' },
])}
actionDisplay="dropdown" // inline | dropdown | menu
bulkActions={[
{ key: 'approve', label: 'Approve', permission: 'employee.approve' },
]}
pagination={{ pageSize: 10 }}
sortable
filterable
selectable
loading={false}
emptyText="No employees"
/>
);
}What happens automatically:
- Columns with
permissionare hidden if user lacks access - Row action buttons are filtered by permission
- Empty action column is removed when no actions are allowed
- Bulk actions appear when rows are selected
Step 5: Permission HTML tags
Every HTML element has a permission wrapper — use instead of raw <div>, <span>, etc.
import {
PermissionDiv,
PermissionSpan,
PermissionH1,
PermissionUl,
PermissionLi,
PermissionAnchor,
PermissionInputEl,
PermissionWrapper,
createPermissionElement,
} from '@multisystemsuite/react-permission-kit';
<PermissionDiv permission="dashboard.view" className="page">
<PermissionH1 permission="dashboard.view">Dashboard</PermissionH1>
<PermissionUl>
<PermissionLi permission="users.read">Users</PermissionLi>
<PermissionLi permission="reports.read">Reports</PermissionLi>
</PermissionUl>
<PermissionAnchor permission="admin.link" href="/admin">
Admin Panel
</PermissionAnchor>
<PermissionInputEl permission="users.edit" type="text" placeholder="Name" />
</PermissionDiv>
{/* Any HTML tag via `as` prop */}
<PermissionWrapper as="nav" permission="nav.view">
<a href="/">Home</a>
</PermissionWrapper>
{/* Create custom tag wrapper */}
const PermissionMark = createPermissionElement('mark');
<PermissionMark permission="highlight">Important text</PermissionMark>Common tags: PermissionDiv, PermissionSpan, PermissionP, PermissionH1–H6, PermissionUl, PermissionOl, PermissionLi, PermissionNav, PermissionHeader, PermissionFooter, PermissionSectionEl, PermissionImg, PermissionFormEl.
Full list → docs/HTML_TAGS.md
Step 6: Forms & fields
import { PermissionForm, PermissionField } from '@multisystemsuite/react-permission-kit';
{/* Auto hide/disable fields from config */}
<PermissionForm
disableMode="hide" // hide | disable | readonly
fields={[
{ name: 'name', permission: 'employee.edit', label: 'Name' },
{ name: 'salary', permission: 'employee.salary.edit', label: 'Salary' },
]}
onSubmit={handleSubmit}
>
{/* or manual fields */}
<PermissionField
permission="employee.salary.edit"
readonlyPermission="employee.salary.view"
hiddenPermission="employee.ssn.view"
label="Salary"
>
<input name="salary" />
</PermissionField>
</PermissionForm>Step 7: Menus, tabs, modals
import {
PermissionMenu,
PermissionTabs,
PermissionModal,
PermissionDialog,
PermissionDropdown,
PermissionToolbar,
PermissionSidebar,
PermissionFAB,
} from '@multisystemsuite/react-permission-kit';
{/* Menu — unauthorized items removed automatically */}
<PermissionMenu
items={[
{ key: 'home', label: 'Home' },
{ key: 'admin', label: 'Admin', permission: 'admin.view' },
{ key: 'reports', label: 'Reports', permission: 'reports.view' },
]}
/>
{/* Tabs — unauthorized tabs hidden */}
<PermissionTabs
tabs={[
{ key: 'dash', label: 'Dashboard', permission: 'dashboard.view', children: <Dashboard /> },
{ key: 'admin', label: 'Admin', permission: 'admin.view', children: <Admin /> },
]}
/>
{/* Modal — only opens if permission exists */}
<PermissionModal permission="users.delete" open={open} onClose={close} title="Confirm">
Are you sure?
</PermissionModal>
{/* Dialog with confirm/cancel */}
<PermissionDialog
permission="users.delete"
open={open}
message="Delete this user?"
onConfirm={handleDelete}
onCancel={close}
/>
{/* Toolbar actions */}
<PermissionToolbar
actions={[
{ key: 'export', label: 'Export', permission: 'reports.export' },
{ key: 'import', label: 'Import', permission: 'reports.import' },
{ key: 'delete', label: 'Delete', permission: 'reports.delete' },
]}
/>
{/* Floating action button */}
<PermissionFAB permission="users.create" position="bottom-right">
+
</PermissionFAB>Step 8: Route guards
React Router v6/v7:
import { PermissionRoute } from '@multisystemsuite/react-permission-kit';
import { Route } from 'react-router-dom';
<Route
path="/admin"
element={
<PermissionRoute
permission="admin.access"
redirectTo="/login"
fallback={<p>Unauthorized</p>}
element={<AdminPanel />}
/>
}
/>Next.js middleware:
// middleware.ts
import { createNextMiddlewareGuard } from '@multisystemsuite/react-permission-kit';
export const middleware = createNextMiddlewareGuard({
publicPaths: ['/login', '/public'],
loginPath: '/login',
getPermissions: async (request) => {
// decode JWT / read session and return permissions[]
return [];
},
});Protected page wrapper:
import { Protected } from '@multisystemsuite/react-permission-kit';
<Protected permission="admin.access" redirectTo="/login" fallback={<AccessDenied />}>
<AdminPage />
</Protected>Step 9: MUI / Ant Design / other UI libraries
This package has zero UI dependencies. Wrap any third-party component:
// MUI Button with Can
import Button from '@mui/material/Button';
import { Can, usePermission } from '@multisystemsuite/react-permission-kit';
<Can permission="users.create">
<Button variant="contained">Create</Button>
</Can>
// MUI DataGrid — filter columns with can()
const { can } = usePermission();
const columns = [
{ field: 'name', headerName: 'Name' },
...(can('employee.salary.view') ? [{ field: 'salary', headerName: 'Salary' }] : []),
];
// PermissionTable + MUI Chip inside cells
import Chip from '@mui/material/Chip';
<PermissionTable
data={employees}
rowKey="id"
columns={[
{ key: 'name', title: 'Name' },
{
key: 'salary',
title: 'Salary',
permission: 'employee.salary.view',
render: (v) => <Chip label={`$${v}`} size="small" />,
},
]}
actions={definePermissionActions([
{ key: 'edit', label: 'Edit', permission: 'employee.update' },
])}
/>Full guides:
- docs/INTEGRATIONS.md — MUI, Ant Design, Chakra, shadcn
- examples/mui/ — MUI examples
- examples/antd/ — Ant Design examples
Step 10: Custom styling
This library ships no default CSS. You control all styling with your own classes, inline styles, or UI framework.
HTML tag components (PermissionDiv, PermissionButton, etc.) accept every native HTML prop — className, style, id, data-*, and more:
<PermissionDiv
permission="dashboard.view"
className="page card"
style={{ padding: 16, background: '#f5f5f5' }}
>
Dashboard
</PermissionDiv>
<PermissionButton
permission="users.delete"
className="btn btn-danger"
style={{ marginTop: 8 }}
>
Delete
</PermissionButton>Any HTML tag via PermissionWrapper:
<PermissionWrapper
as="nav"
permission="nav.view"
className="sidebar"
style={{ width: 240 }}
>
Menu items
</PermissionWrapper>Built-in UI components — pass className on the root element; button/link components also accept full native HTML attributes:
<PermissionTable className="my-table" data={rows} columns={columns} />
<PermissionCard className="card card--elevated" permission="reports.view" title="Reports" />
<PermissionModal className="modal modal--lg" permission="settings.open" title="Settings" />Can / Cannot / Protected — no wrapper element; style the child directly:
<Can permission="reports.view">
<div className="report-panel" style={{ minHeight: 400 }}>
Report content
</div>
</Can>Third-party components (MUI, Ant Design, Chakra, shadcn) — use PermissionRender or wrap with Can and style your component as usual:
import Button from '@mui/material/Button';
import { PermissionRender } from '@multisystemsuite/react-permission-kit';
<PermissionRender
permission="users.delete"
render={(allowed) =>
allowed ? (
<Button className="mui-delete-btn" color="error" variant="contained">
Delete
</Button>
) : null
}
/>Custom HTML tag factory — create and style any tag:
import { createPermissionElement } from '@multisystemsuite/react-permission-kit';
const PermissionMark = createPermissionElement('mark');
<PermissionMark
permission="highlight"
className="highlight-yellow"
style={{ fontWeight: 600 }}
>
Important
</PermissionMark>When permission is denied, interactive elements may receive data-permission-denied (and disabled / readOnly depending on deny mode) — use these for custom disabled/hidden styles in your CSS.
Quick Start
import { PermissionProvider, Can, usePermission, PermissionButton } from '@multisystemsuite/react-permission-kit';
function App() {
return (
<PermissionProvider
permissions={['users.read', 'users.create', 'dashboard.view']}
roles={['editor']}
tenant="company-a"
organization="india"
department="qa"
storage="memory"
>
<Dashboard />
</PermissionProvider>
);
}
function Dashboard() {
const { can, cannot, hasRole, isSuperAdmin, refresh } = usePermission();
return (
<div>
<Can permission="dashboard.view">
<h1>Dashboard</h1>
</Can>
{can('users.read') && <UserList />}
<PermissionButton permission="users.create" hide tooltip="No create access">
Create User
</PermissionButton>
</div>
);
}Provider
<PermissionProvider
permissions={[]}
roles={[]}
rolePermissions={{ admin: ['admin.*'], editor: ['posts.edit'] }}
featureFlags={['feature.chat']}
isSuperAdmin={false}
tenant="company-a"
organization="india"
department="qa"
storage="memory" // memory | localStorage | sessionStorage | custom
storageKey="react-permission-kit"
jwt={accessToken}
onLoad={async () => ({ permissions: [], roles: [] })}
onPermissionChange={(state) => console.log(state)}
>
<App />
</PermissionProvider>usePermission Hook
const {
can, // (permission: string) => boolean
cannot, // (permission: string) => boolean
canAny, // (permissions: string[]) => boolean
canAll, // (permissions: string[]) => boolean
hasRole, // (role: string) => boolean
hasAnyRole, // (roles: string[]) => boolean
hasAllRoles, // (roles: string[]) => boolean
hasFeature, // (feature: string) => boolean
isSuperAdmin, // () => boolean
refresh, // () => Promise<void>
setPermissions,// (permissions: string[]) => void
setRoles, // (roles: string[]) => void
loading,
} = usePermission();Components
| Component | Description |
|-----------|-------------|
| <Can> | Render children when allowed |
| <Cannot> | Render when denied |
| <Protected> | Block content with fallback/redirect |
| <PermissionButton> | Button with hide/disable/readonly modes |
| <PermissionLink> | Permission-aware anchor |
| <PermissionRoute> | Router-agnostic route guard |
| <PermissionTable> | Table with auto-filtered columns/actions |
| <PermissionMenu> | Auto-filtered menu |
| <PermissionTabs> | Auto-filtered tabs |
| <PermissionModal> | Permission-gated modal |
| <PermissionForm> | Auto hide/disable form fields |
See API Documentation for the full list.
Advanced UI Components
All 20 enterprise components are included with built-in permission filtering.
PermissionTable
Auto-hides unauthorized columns and row actions. Supports selection, pagination, sorting, filtering, server-side mode, expandable rows, bulk actions, and responsive layout.
<PermissionTable
permission="employees.view"
data={employees}
rowKey="id"
columns={[
{ key: 'name', title: 'Name' },
{ key: 'salary', title: 'Salary', permission: 'employee.salary.view' },
]}
actions={definePermissionActions([
{ key: 'edit', label: 'Edit', permission: 'employee.update', icon: <EditIcon /> },
{ key: 'delete', label: 'Delete', permission: 'employee.delete', danger: true },
])}
actionDisplay="dropdown" // inline | dropdown | menu
bulkActions={[{ key: 'delete', label: 'Delete', permission: 'employee.bulk.delete' }]}
pagination={{ pageSize: 10 }}
sortable
filterable
selectable
expandable={{ expandedRowRender: (row) => <Detail row={row} /> }}
loading={isLoading}
emptyText="No employees"
/>The library automatically:
- Removes unauthorized columns (
PermissionColumnpermission prop) - Removes unauthorized action buttons
- Removes the actions column when no row has visible actions
- Supports icon-only actions (
iconOnly: true) - Supports dropdown and row menu action display
- Supports bulk actions on selected rows
PermissionButton
Modes: hide, disable, readonly via props or hideMode. Supports tooltip, icon, and isLoading.
<PermissionButton permission="users.create" hide tooltip="No access" icon={<PlusIcon />} isLoading={saving}>
Create
</PermissionButton>PermissionField / PermissionForm
<PermissionForm
disableMode="hide"
fields={[
{ name: 'salary', permission: 'employee.salary.edit', label: 'Salary' },
{ name: 'ssn', hiddenPermission: 'employee.ssn.view' },
]}
/>PermissionRender
Render by permission, role, or feature flag:
<PermissionRender
permission="dashboard.view"
feature="feature.analytics"
render={(allowed) => (allowed ? <Dashboard /> : <Upgrade />)}
/>See docs/API.md for PermissionCard, PermissionMenu, PermissionSidebar, PermissionTabs, PermissionModal, PermissionDialog, PermissionDropdown, PermissionToolbar, PermissionFAB, PermissionImage, PermissionSection, PermissionSkeleton, and PermissionBadge.
MUI, Ant Design & other UI libraries
This package has no UI dependency. Use it with MUI, Ant Design, Chakra, shadcn/ui, or any kit:
| Approach | Example |
|----------|---------|
| usePermission() | Filter MUI DataGrid columns with can('employee.salary.view') |
| <Can> wrapper | <Can permission="x"><MuiButton /></Can> |
| PermissionTable | Permission logic + MUI Chip/Icon in column render |
// MUI Button
import Button from '@mui/material/Button';
import { Can } from '@multisystemsuite/react-permission-kit';
<Can permission="users.create">
<Button variant="contained">Create</Button>
</Can>
// PermissionTable + MUI inside cells
<PermissionTable
data={employees}
rowKey="id"
columns={[
{ key: 'name', title: 'Name' },
{
key: 'salary',
title: 'Salary',
permission: 'employee.salary.view',
render: (v) => <Chip label={`$${v}`} size="small" />,
},
]}
actions={definePermissionActions([
{ key: 'edit', label: 'Edit', permission: 'employee.update', icon: <EditIcon /> },
])}
/>Full guide: docs/INTEGRATIONS.md
Examples: examples/mui/, examples/antd/, examples/chakra/, examples/shadcn/
Permission HTML tags (all elements)
Every HTML tag has a permission wrapper: PermissionDiv, PermissionSpan, PermissionH1, PermissionUl, PermissionAnchor, and 80+ more.
import { PermissionDiv, PermissionUl, PermissionLi, PermissionWrapper } from '@multisystemsuite/react-permission-kit';
<PermissionDiv permission="dashboard.view">
<PermissionUl>
<PermissionLi permission="users.read">Users</PermissionLi>
<PermissionLi permission="reports.read">Reports</PermissionLi>
</PermissionUl>
</PermissionDiv>
{/* Any tag via `as` */}
<PermissionWrapper as="nav" permission="nav.view">Menu</PermissionWrapper>See docs/HTML_TAGS.md for the full list and createPermissionElement() factory.
Permission Engine
import { checkPermission, matchWildcard, mergePermissions } from '@multisystemsuite/react-permission-kit';
checkPermission('users.read', ['users.*']); // { allowed: true }
matchWildcard('admin.*', 'reports.export'); // true
mergePermissions(['a'], ['b']); // ['a', 'b']Permission Examples
users.read,users.create,users.update,users.deletedashboard.view,reports.exportadmin.*,users.*inventory.stock.edit,sales.order.approvefeature.chat,feature.ai
Route Guards
React Router v6/v7
<Route
path="/admin"
element={
<PermissionRoute
permission="admin.access"
redirectTo="/login"
element={<AdminPanel />}
/>
}
/>Next.js Middleware
import { createNextMiddlewareGuard } from '@multisystemsuite/react-permission-kit';
export const middleware = createNextMiddlewareGuard({
publicPaths: ['/login'],
loginPath: '/login',
getPermissions: async (request) => [],
});SSR
Use storage="memory" on the server and hydrate with client permissions:
<PermissionProvider permissions={serverPermissions} storage="memory">
{children}
</PermissionProvider>Documentation
- How to Use — this README
- API Reference
- HTML Tags Guide — all Permission* HTML components
- UI Integrations — MUI, Ant Design, Chakra, shadcn
- Migration Guide
- FAQ
- Best Practices
- Security Guide
- Examples
Development
npm install
npm run build
npm test
npm run test:coverage
npm run lint
npm run docsPublishing
This package uses Changesets for versioning:
npm run changeset # Add a changeset
npm run version-packages
npm run release # Build and publish to npmSet NPM_TOKEN in GitHub secrets for automated publishing via GitHub Actions.
License
MIT © MultiSystemSuite
