react-permgate
v1.0.2
Published
Declarative, TypeScript-first, role/permission based render management for React
Readme
react-permgate
Declarative, TypeScript-first, role/permission based render management for React.
Features
- ✅
<PermissionGate>— declarative component API - ✅
<PermissionSwitch>— render different UI per role - ✅
usePermission/usePermissions— hook API - ✅
withPermission— Higher-Order Component API - ✅
AsyncPermissionProvider— fetch permissions from an API - ✅ Zero runtime dependencies (React peer dep only)
- ✅ Full TypeScript support with generics
- ✅ Works with React 17+
Installation
npm install react-permgateQuick Start
1. Wrap your app with PermissionProvider
import { PermissionProvider } from 'react-permgate';
function App() {
const userPermissions = ['editor', 'dashboard:view'];
return (
<PermissionProvider permissions={userPermissions}>
<YourApp />
</PermissionProvider>
);
}2. Use <PermissionGate> to protect UI
import { PermissionGate } from 'react-permgate';
function Dashboard() {
return (
<div>
{/* Only admins see this */}
<PermissionGate requires="admin">
<DeleteUserButton />
</PermissionGate>
{/* With a fallback */}
<PermissionGate requires="admin" fallback={<p>You don't have access.</p>}>
<AdminPanel />
</PermissionGate>
{/* Requires ANY of the permissions */}
<PermissionGate requires={['admin', 'editor']} mode="any">
<EditButton />
</PermissionGate>
</div>
);
}API Reference
<PermissionProvider>
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| permissions | string[] | ✅ | List of permissions the current user has |
| children | ReactNode | ✅ | Your app tree |
<PermissionGate>
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| requires | string \| string[] | — | Required permission(s) |
| mode | 'all' \| 'any' | 'all' | How to check multiple permissions |
| fallback | ReactNode | null | Rendered when check fails |
| children | ReactNode | — | Rendered when check passes |
<PermissionSwitch>
Renders the first matching <PermissionCase>. Falls back to <PermissionDefault> if none match.
import { PermissionSwitch, PermissionCase, PermissionDefault } from 'react-permgate';
<PermissionSwitch>
<PermissionCase requires="admin">
<AdminDashboard />
</PermissionCase>
<PermissionCase requires="editor">
<EditorDashboard />
</PermissionCase>
<PermissionDefault>
<ReadOnlyView />
</PermissionDefault>
</PermissionSwitch>usePermission(requires, mode?)
const canDelete = usePermission('admin');
const canEdit = usePermission(['editor', 'admin'], 'any');usePermissions()
const permissions = usePermissions();
// ['admin', 'billing:read', ...]withPermission(Component, requires, mode?, Fallback?)
const AdminPanel = withPermission(Panel, 'admin');
const EditorView = withPermission(View, ['editor', 'admin'], 'any', FallbackComponent);The wrapped component gets displayName set automatically: withPermission(Panel)
<AsyncPermissionProvider>
<AsyncPermissionProvider
fetchPermissions={async () => {
const res = await fetch('/api/me/permissions');
return res.json();
}}
loadingFallback={<Spinner />}
errorFallback={<p>Failed to load permissions.</p>}
>
<App />
</AsyncPermissionProvider>TypeScript: Custom Permission Types
type AppPermission = 'admin' | 'editor' | 'viewer' | 'billing:read';
<PermissionProvider<AppPermission> permissions={['admin', 'billing:read']}>
<App />
</PermissionProvider>
// TypeScript will now catch typos at compile time:
<PermissionGate<AppPermission> requires="adminn"> // ❌ Type error!
<PermissionGate<AppPermission> requires="admin"> // ✅Error Handling
Using any hook outside of <PermissionProvider> throws:
[react-permgate] hooks must be used inside <PermissionProvider>License
MIT
