A flexible, lightweight React library for implementing permission-based access control and feature flags in your applications.
- π Permission-based Access Control - Granular control over what users can see and do
- π© Feature Flags - Toggle features on/off for different user groups
- π‘οΈ Route Protection - Protect entire routes based on permissions
- πΎ Flexible Storage - Multiple storage adapters (localStorage, sessionStorage, AsyncStorage, IndexedDB)
- π Security - Encrypted storage options available
- π Framework Agnostic - Works with Next.js, React Router, and other routing solutions
- π± React Native Support - Full support for React Native applications
- π― TypeScript First - Built with TypeScript for excellent developer experience
npm install @clywell/react-access-control
# or
yarn add @clywell/react-access-control
# or
pnpm add @clywell/react-access-controlimport { AccessControlProvider } from '@clywell/react-access-control';
function App() {
return (
<AccessControlProvider>
<YourApp />
</AccessControlProvider>
);
}import { useAccessControl } from '@clywell/react-access-control';
function LoginComponent() {
const { setUserContext } = useAccessControl();
const handleLogin = async (credentials) => {
const user = await authenticateUser(credentials);
setUserContext({
user: { id: user.id, name: user.name },
permissions: user.permissions, // ['READ_USERS', 'WRITE_POSTS', etc.]
featureFlags: user.featureFlags // ['BETA_UI', 'NEW_DASHBOARD', etc.]
});
};
return (
// Your login form
);
}import { AccessGuard } from '@clywell/react-access-control';
function UserManagement() {
return (
<div>
{/* Always visible */}
<h1>User Management</h1>
{/* Only visible with READ_USERS permission */}
<AccessGuard permissions={['READ_USERS']}>
<UserList />
</AccessGuard>
{/* Only visible with WRITE_USERS permission */}
<AccessGuard permissions={['WRITE_USERS']}>
<AddUserButton />
</AccessGuard>
{/* Only visible when BETA_UI feature flag is enabled */}
<AccessGuard featureFlags={['BETA_UI']}>
<NewUserInterface />
</AccessGuard>
</div>
);
}import { RouteGuard } from '@clywell/react-access-control';
function AdminRoute() {
return (
<RouteGuard
permissions={['ACCESS_ADMIN']}
fallbackPath="/unauthorized"
>
<AdminDashboard />
</RouteGuard>
);
}import { AccessControlProvider, LocalStorageAdapter } from '@clywell/react-access-control';
// Use localStorage with custom prefix
const storageAdapter = new LocalStorageAdapter('my-app');
function App() {
return (
<AccessControlProvider storageAdapter={storageAdapter}>
<YourApp />
</AccessControlProvider>
);
}import {
AccessControlProvider,
LocalStorageAdapter,
EncryptedStorageAdapter
} from '@clywell/react-access-control';
const baseAdapter = new LocalStorageAdapter('my-app');
const encryptedAdapter = new EncryptedStorageAdapter(baseAdapter, 'your-secret-key');
function App() {
return (
<AccessControlProvider storageAdapter={encryptedAdapter}>
<YourApp />
</AccessControlProvider>
);
}import AsyncStorage from '@react-native-async-storage/async-storage';
import {
AccessControlProvider,
AsyncStorageAdapter
} from '@clywell/react-access-control';
const storageAdapter = new AsyncStorageAdapter(AsyncStorage);
function App() {
return (
<AccessControlProvider storageAdapter={storageAdapter}>
<YourApp />
</AccessControlProvider>
);
}import {
AccessControlProvider,
MultiTierStorageAdapter,
IndexedDBStorageAdapter,
LocalStorageAdapter
} from '@clywell/react-access-control';
// Try IndexedDB first, fallback to localStorage
const storageAdapter = new MultiTierStorageAdapter([
new IndexedDBStorageAdapter(),
new LocalStorageAdapter()
]);
function App() {
return (
<AccessControlProvider storageAdapter={storageAdapter}>
<YourApp />
</AccessControlProvider>
);
}import { NextJSAdapter } from '@clywell/react-access-control/adapters/nextjs';
const navigationAdapter = new NextJSAdapter();
function App() {
return (
<AccessControlProvider navigationAdapter={navigationAdapter}>
<YourApp />
</AccessControlProvider>
);
}import { ReactRouterAdapter } from '@clywell/react-access-control/adapters/react-router';
import { useNavigate } from 'react-router-dom';
function App() {
const navigate = useNavigate();
const navigationAdapter = new ReactRouterAdapter(navigate);
return (
<AccessControlProvider navigationAdapter={navigationAdapter}>
<YourApp />
</AccessControlProvider>
);
}| Prop | Type | Description |
|---|---|---|
storageAdapter |
StorageAdapter |
Custom storage implementation |
navigationAdapter |
NavigationAdapter |
Custom navigation implementation |
children |
ReactNode |
Your app components |
| Prop | Type | Description |
|---|---|---|
permissions |
string[] |
Required permissions (OR logic) |
featureFlags |
string[] |
Required feature flags (OR logic) |
requireAll |
boolean |
Use AND logic instead of OR |
fallback |
ReactNode |
Fallback component when access denied |
children |
ReactNode |
Content to protect |
| Prop | Type | Description |
|---|---|---|
permissions |
string[] |
Required permissions |
featureFlags |
string[] |
Required feature flags |
requireAll |
boolean |
Use AND logic instead of OR |
fallbackPath |
string |
Redirect path when access denied |
fallback |
ReactNode |
Fallback component instead of redirect |
children |
ReactNode |
Content to protect |
const {
user, // Current user info
permissions, // User's permissions array
featureFlags, // User's feature flags array
hasPermission, // (permission: string) => boolean
hasFeatureFlag, // (flag: string) => boolean
hasAnyPermission, // (permissions: string[]) => boolean
hasAllPermissions, // (permissions: string[]) => boolean
hasAnyFeatureFlag, // (flags: string[]) => boolean
hasAllFeatureFlags,// (flags: string[]) => boolean
setUserContext, // (context: UserContext) => void
updatePermissions, // (permissions: string[]) => void
updateFeatureFlags,// (flags: string[]) => void
logout, // () => void
isLoading // boolean
} = useAccessControl();- LocalStorageAdapter - Browser localStorage
- SessionStorageAdapter - Browser sessionStorage
- AsyncStorageAdapter - React Native AsyncStorage
- IndexedDBStorageAdapter - Browser IndexedDB
- EncryptedStorageAdapter - Encrypted wrapper for any adapter
- MultiTierStorageAdapter - Multiple adapters with fallback
import { StorageAdapter } from '@clywell/react-access-control';
class CustomStorageAdapter implements StorageAdapter {
async getItem(key: string): Promise<string | null> {
// Your implementation
}
async setItem(key: string, value: string): Promise<void> {
// Your implementation
}
async removeItem(key: string): Promise<void> {
// Your implementation
}
}Check out the examples directory for complete working examples:
- Basic Usage - Simple permission and feature flag demo
- Next.js Integration - App Router with server-side access control
- React Native - Mobile app integration
We welcome contributions! Please see our Contributing Guide for details.
MIT Β© Clywell
- π Documentation
- π Issues
- π¬ Discussions