@rqdhw3n/react-access-control
v1.0.2
Published
Lightweight access control utilities for React apps with roles, permissions, hooks, and conditional rendering.
Maintainers
Readme
@rqdhw3n/react-access-control
Lightweight access control utilities for React applications. The package provides a small context provider, a declarative Can component, focused hooks, pure utility functions, and an optional React Router entrypoint for page protection.
GitHub: https://github.com/rqdhw3ns
Features
- Tiny runtime footprint
- React 18+ compatible
- Works with Vite, Next.js, CRA, and TypeScript React apps
- Roles and permissions support
- Declarative and programmatic access checks
- Optional React Router v6 route protection
- Shared access context across both the main package and router entrypoint
- ESM, CJS, and TypeScript declaration output
Installation
Install the core package:
npm install @rqdhw3n/react-access-controlIf you want route protection, install React Router too:
npm install @rqdhw3n/react-access-control react-router-domyarn add @rqdhw3n/react-access-control react-router-dompnpm add @rqdhw3n/react-access-control react-router-domQuick Start
import {
AccessProvider,
Can,
useCanPermission
} from '@rqdhw3n/react-access-control';
function DeleteButton() {
const canDeleteUsers = useCanPermission('users.delete');
if (!canDeleteUsers) {
return null;
}
return <button type="button">Delete user</button>;
}
export function App() {
return (
<AccessProvider
roles={['admin']}
permissions={['users.create', 'users.delete']}
>
<Can role="admin">
<section>Admin panel</section>
</Can>
<DeleteButton />
</AccessProvider>
);
}AccessProvider
Wrap your app with AccessProvider to make the current user's roles and permissions available throughout the tree.
import { AccessProvider } from '@rqdhw3n/react-access-control';
<AccessProvider
roles={['admin']}
permissions={['users.create', 'users.delete']}
>
<App />
</AccessProvider>;Can Component
Render children only when the access requirement is satisfied.
import { Can } from '@rqdhw3n/react-access-control';
<Can role="admin">
<AdminPanel />
</Can>;<Can permission="users.delete" fallback={<NoAccess />}>
<DeleteButton />
</Can>;<Can permissions={['users.create', 'users.update']} requireAll>
<UserActions />
</Can>;You can combine role and permission requirements. When requireAll is false, matching any supplied role or permission is enough. When requireAll is true, every supplied role and permission requirement must match.
ProtectedRoute
ProtectedRoute is available from a separate router entrypoint so apps that do not use React Router do not need the dependency.
It uses the same AccessProvider context as the main package, so one provider can drive both component-level and route-level access checks.
import { ProtectedRoute } from '@rqdhw3n/react-access-control/router';ProtectedRoute must be rendered inside AccessProvider.
Basic Example
import { AccessProvider } from '@rqdhw3n/react-access-control';
import { ProtectedRoute } from '@rqdhw3n/react-access-control/router';
<AccessProvider
roles={['admin']}
permissions={['users.view', 'users.delete']}
>
<ProtectedRoute permission="users.delete" redirectTo="/">
<DeleteUsersPage />
</ProtectedRoute>
</AccessProvider>;React Router v6 Example
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { AccessProvider } from '@rqdhw3n/react-access-control';
import { ProtectedRoute } from '@rqdhw3n/react-access-control/router';
function App() {
return (
<AccessProvider
roles={['admin']}
permissions={['dashboard.view', 'users.view', 'users.delete']}
>
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/dashboard"
element={
<ProtectedRoute permission="dashboard.view" redirectTo="/">
<DashboardPage />
</ProtectedRoute>
}
/>
<Route
path="/admin"
element={
<ProtectedRoute role="admin" redirectTo="/">
<AdminPage />
</ProtectedRoute>
}
/>
<Route
path="/users/delete"
element={
<ProtectedRoute permission="users.delete" redirectTo="/">
<DeleteUsersPage />
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
</AccessProvider>
);
}Redirect Example
<ProtectedRoute permission="users.delete" redirectTo="/">
<DeleteUsersPage />
</ProtectedRoute>Fallback Example
<ProtectedRoute role="admin" fallback={<NoAccessPage />}>
<AdminPage />
</ProtectedRoute>If access is denied, ProtectedRoute behaves like this:
redirectToprovided: renders React Router's<Navigate />fallbackprovided withoutredirectTo: renders the fallback- neither provided: returns
null
Hooks
useAccess()
import { useAccess } from '@rqdhw3n/react-access-control';
function Summary() {
const { roles, permissions, canAccess } = useAccess();
return (
<pre>
{JSON.stringify(
{
roles,
permissions,
canManageUsers: canAccess({ permission: 'users.manage' })
},
null,
2
)}
</pre>
);
}useCanRole(role)
import { useCanRole } from '@rqdhw3n/react-access-control';
function AdminBadge() {
const isAdmin = useCanRole('admin');
return isAdmin ? <span>Admin</span> : null;
}useCanPermission(permission)
import { useCanPermission } from '@rqdhw3n/react-access-control';
function CreateUserButton() {
const canCreateUser = useCanPermission('users.create');
return canCreateUser ? <button>Create user</button> : null;
}useCanAccess(options)
import { useCanAccess } from '@rqdhw3n/react-access-control';
function UserActions() {
const canManageUsers = useCanAccess({
roles: ['admin', 'manager'],
permissions: ['users.create', 'users.update'],
requireAll: false
});
return canManageUsers ? <div>User actions</div> : null;
}Utility Functions
All utility functions are pure and can be used outside React.
import {
hasRole,
hasPermission,
canAccess
} from '@rqdhw3n/react-access-control';
hasRole(['admin', 'editor'], ['admin']);
hasPermission(['users.create'], ['users.create', 'users.update'], false);
canAccess({
userRoles: ['manager'],
userPermissions: ['reports.view'],
roles: ['admin', 'manager'],
permission: 'reports.view'
});Props
AccessProviderProps
| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| roles | string[] | No | Current user roles |
| permissions | string[] | No | Current user permissions |
| children | ReactNode | Yes | React tree that receives access context |
CanProps
| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| role | string | No | Single required role |
| roles | string[] | No | Multiple required roles |
| permission | string | No | Single required permission |
| permissions | string[] | No | Multiple required permissions |
| requireAll | boolean | No | Require all supplied checks to pass |
| fallback | ReactNode | No | Rendered when access is denied |
| children | ReactNode | Yes | Rendered when access is allowed |
ProtectedRouteProps
| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| role | string | No | Single required role |
| roles | string[] | No | Multiple required roles |
| permission | string | No | Single required permission |
| permissions | string[] | No | Multiple required permissions |
| requireAll | boolean | No | Require all supplied checks to pass |
| redirectTo | string | No | Redirect path when access is denied |
| fallback | ReactNode | No | Rendered when access is denied and no redirect is used |
| children | ReactNode | Yes | Rendered when access is allowed |
TypeScript
The package ships with full type declarations and exports the main public types:
import type {
AccessProviderProps,
CanProps,
AccessContextValue,
AccessCheckOptions,
ProtectedRouteProps
} from '@rqdhw3n/react-access-control';Security Note
This package only controls client-side rendering and navigation. Your backend must still enforce roles, permissions, and authorization rules for every protected action and route.
Example App
A minimal Vite example app is included in example/ for local development and manual testing.
License
MIT
