@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
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