@can
directive pattern from Laravel Blade*
), single chars (?
), complex expressions||
, &&
, |
, &
)npm install @devwizard/laravel-react-permissions
# or
yarn add @devwizard/laravel-react-permissions
# or
pnpm add @devwizard/laravel-react-permissions
Add permissions to your Inertia middleware:
// app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user() ? [
'id' => $request->user()->id,
'name' => $request->user()->name,
'email' => $request->user()->email,
'permissions' => $request->user()->getAllPermissions()->pluck('name')->toArray(),
] : null,
],
];
}
import { Can, usePermissions } from '@devwizard/laravel-react-permissions';
// Basic usage
function UserManagement() {
return (
<Can permission="users.create">
<button>Create User</button>
</Can>
);
}
// Advanced patterns
function AdminPanel() {
const { hasPermission } = usePermissions();
const canAccess = hasPermission('admin.* || moderator.users');
return (
<Can permission="(admin.* || moderator.*) && active.user">
<AdminDashboard />
</Can>
);
}
// Custom permissions
function FeatureFlag() {
const featurePermissions = ['feature.beta', 'feature.advanced'];
return (
<Can
permission="feature.beta"
permissions={featurePermissions}
fallback={<div>Feature not available</div>}
>
<BetaFeature />
</Can>
);
}
<Can>
ComponentReact component for conditional rendering based on permissions:
<Can permission="users.* || admin.access" fallback={<div>Access denied</div>}>
<UserManagement />
</Can>
Props:
permission?: string | boolean
- Permission to check (supports patterns and boolean logic)permissions?: string[]
- Custom permissions array (overrides auth permissions)fallback?: ReactNode
- What to render when access is deniedchildren: ReactNode
- Content to render when access is grantedusePermissions
HookHook for programmatic permission checking:
const { hasPermission, permissions } = usePermissions();
const canEdit = hasPermission('posts.edit || posts.moderate');
// With custom permissions
const { hasPermission } = usePermissions(['custom.perm', 'another.perm']);
Parameters:
permissions?: string[]
- Optional custom permissions arrayReturns:
hasPermission: (permission: string | boolean) => boolean
- Check permission functionpermissions: string[]
- Current permissions arrayhasAnyPermission: (permissions: string[]) => boolean
- Check any of multiple permissionshasAllPermissions: (permissions: string[]) => boolean
- Check all permissionsisAuthenticated: boolean
- Authentication statuswithPermission
HOCHigher-order component for wrapping components:
const ProtectedComponent = withPermission(MyComponent, 'admin.access', <Unauthorized />);
// With custom permissions
const ProtectedFeature = withPermission(
FeatureComponent,
'feature.beta',
<div>Feature not available</div>,
['feature.beta', 'admin.override']
);
*
) - Match Any Characters<Can permission="users.*"> {/* users.create, users.edit, users.delete */}
<Can permission="admin.*"> {/* admin.users, admin.settings, admin.logs */}
<Can permission="*.create"> {/* users.create, posts.create, etc. */}
?
) - Match One Character<Can permission="user?.edit"> {/* user1.edit, user2.edit, userA.edit */}
<Can permission="level?.access"> {/* level1.access, level2.access, etc. */}
<Can permission="users.edit || posts.edit"> {/* OR logic */}
<Can permission="admin.access && users.manage"> {/* AND logic */}
<Can permission="(users.* || posts.*) && active"> {/* Grouped expressions */}
<Can permission="true"> {/* Always allow */}
<Can permission="false"> {/* Always deny */}
{
/* Multiple operators */
}
<Can permission="(admin.* && active) || (moderator.* && verified)">
<SensitiveData />
</Can>;
{
/* Pattern combinations */
}
<Can permission="*.create || admin.* || super.user">
<CreateButton />
</Can>;
{
/* Business logic */
}
<Can permission="(department.hr && level.manager) || admin.override">
<EmployeeData />
</Can>;
function TenantDashboard() {
const [tenantPermissions, setTenantPermissions] = useState([]);
useEffect(() => {
fetchTenantPermissions().then(setTenantPermissions);
}, []);
return (
<Can permission="tenant.admin || tenant.manager" permissions={tenantPermissions}>
<TenantControls />
</Can>
);
}
const featureFlags = ['feature.newUi', 'feature.advancedSearch'];
<Can permission="feature.newUi" permissions={featureFlags}>
<NewUserInterface />
</Can>;
// Mock permissions for testing
const mockPermissions = ['users.view', 'posts.create'];
<Can permission="users.view" permissions={mockPermissions}>
<TestComponent />
</Can>
// No permissions (empty array)
<Can permission="admin.access" permissions={[]}>
<div>Should not render</div>
</Can>
// From localStorage
const savedPermissions = JSON.parse(localStorage.getItem('permissions') || '[]');
// From API response
const apiPermissions = user?.customPermissions || [];
// From configuration
const configPermissions = ['feature.beta', 'admin.access'];
<Can permission="feature.beta" permissions={configPermissions}>
<BetaFeature />
</Can>;
Full TypeScript support with comprehensive type definitions:
import type {
CanProps,
UsePermissionsReturn,
WithPermissionOptions,
} from '@devwizard/laravel-react-permissions';
// Typed component
const MyPermissionComponent: React.FC<CanProps> = ({ permission, permissions, children }) => {
return (
<Can permission={permission} permissions={permissions}>
{children}
</Can>
);
};
// Typed hook usage
const PermissionChecker: React.FC = () => {
const { hasPermission, permissions, isAuthenticated }: UsePermissionsReturn = usePermissions();
return <div>{isAuthenticated && hasPermission('users.view') && <UserList />}</div>;
};
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature
)git commit -m 'Add some amazing feature'
)git push origin feature/amazing-feature
)MIT License - see the LICENSE file for details.
IQBAL HASAN
@can
directive inspirationMade with β€οΈ by DevWizard
Add permissions to your Inertia middleware:
// app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user() ? [
'id' => $request->user()->id,
'name' => $request->user()->name,
'email' => $request->user()->email,
'permissions' => $request->user()->getAllPermissions()->pluck('name')->toArray(),
] : null,
],
];
}
import { Can, usePermissions } from '@devwizard/laravel-react-permissions';
// Basic usage
function UserManagement() {
return (
<Can permission="users.create">
<button>Create User</button>
</Can>
);
}
// Advanced patterns
function AdminPanel() {
const { hasPermission } = usePermissions();
const canAccess = hasPermission('admin.* || moderator.users');
return (
<Can permission="(admin.* || moderator.*) && active.user">
<AdminDashboard />
</Can>
);
}
// Custom permissions
function FeatureFlag() {
const featurePermissions = ['feature.beta', 'feature.advanced'];
return (
<Can
permission="feature.beta"
permissions={featurePermissions}
fallback={<div>Feature not available</div>}
>
<BetaFeature />
</Can>
);
}
The permissions system supports powerful pattern matching and boolean expressions for maximum flexibility:
<Can permission="true"> {/* Always visible to authenticated users */}
<Can permission="false"> {/* Never visible (useful for testing) */}
||
and |
)<Can permission="users.* || posts.*"> {/* User management OR post management */}
<Can permission="admin.access | manager.access"> {/* Admin OR manager access */}
&&
and &
)<Can permission="users.* && admin.access"> {/* User permissions AND admin access */}
<Can permission="reports.view & security.clearance"> {/* Reports AND security clearance */}
<Can permission="(users.* || posts.*) && admin.access">
{/* (User OR post management) AND admin access */}
</Can>
<Can expression="(admin.* && security.*) || system.override">
{/* Admin with security OR system override */}
</Can>
Instead of always using auth permissions, you can provide your own permissions array:
// Static permissions array
const staticPerms = ['users.create', 'users.edit', 'posts.view'];
<Can
permission="users.create"
permissions={staticPerms}
>
<CreateUserButton />
</Can>
// Dynamic permissions from API or localStorage
<Can
permission="posts.view || posts.edit"
permissions={getDynamicPermissions()}
>
<PostManagement />
</Can>
// Empty permissions (no access)
<Can
permission="admin.access"
permissions={[]}
fallback={<div>No permissions available</div>}
>
<AdminPanel />
</Can>
const customPerms = ['feature.beta', 'admin.override'];
const { hasPermission } = usePermissions(customPerms);
const hasBeta = hasPermission('feature.beta'); // true
const hasAdmin = hasPermission('admin.access'); // false (not in custom array)
permissions={featureFlags}
permissions={configPermissions}
permissions={apiPermissions}
permissions={mockPermissions}
or permissions={[]}
permissions={JSON.parse(localStorage.getItem('perms'))}
*
) - Match Any Characters<Can permission="users.*"> {/* matches: users.create, users.edit, users.delete */}
<Can permission="admin.*"> {/* matches: admin.users, admin.settings, admin.reports */}
?
) - Match One Character<Can permission="user?.edit"> {/* matches: user1.edit, user2.edit, userA.edit */}
<Can permission="level?.view"> {/* matches: level1.view, level2.view, level3.view */}
<Can permission="users.create"> {/* exact match only */}
// Boolean with logical operators
<Can permission="users.* && (admin.access || true)">
<div>User management (admin access helps but not required)</div>
</Can>
// Complex multi-level logic
<Can permission="(admin.* && security.*) || (manager.* && reports.view) || system.override">
<div>Multiple access paths to sensitive data</div>
</Can>
// Feature flags with boolean logic
<Can permission="feature.beta || admin.access">
<div>Beta feature or admin access</div>
</Can>
// Negation (coming soon)
<Can permission="admin.access && !feature.disabled">
<div>Admin access unless feature is disabled</div>
</Can>
const { isValidExpression, checkExpression } = usePermissions();
// Validate expressions before using them
const isValid = isValidExpression('users.* && (admin.access || manager.role)');
const result = checkExpression('(admin.* && security.*) || system.override');
@can
Blade
directive)true
/false
) and logical operators (||
, &&
,
|
, &
)*
), single chars (?
), complex expressionspermissions
folder to your resources/js/
directoryAdd to your HandleInertiaRequests.php
middleware:
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user() ? [
...$request->user()->toArray(),
'permissions' => $request->user()->getAllPermissions()->pluck('name'),
] : null,
],
];
}
The <Can>
component follows Laravelβs @can
convention and supports:
*
) and single chars (?
)||
, &&
, |
, &
)import { Can } from '@/permissions';
<Can permission="users.create">
<button>Create User</button>
</Can>;
import { usePermissions } from '@/permissions';
function MyComponent() {
const { hasPermission } = usePermissions();
return <div>{hasPermission('users.edit') && <button>Edit User</button>}</div>;
}
import { type NavItem } from '@/permissions';
const navItems: NavItem[] = [
{
title: 'Users',
href: '/users',
permission: 'users.view',
},
{
title: 'Admin',
href: '/admin',
pattern: 'admin.*',
},
];
import { withPermission } from '@/permissions';
const ProtectedComponent = withPermission(MyComponent, {
permission: 'admin.access',
fallback: <div>Access denied</div>,
});
Props:
permission?: string
- Single permission (supports patterns, boolean values, and logical
operators)expression?: string
- Complex boolean expression with logical operatorsanyPermissions?: string[]
- User needs ANY of these (each can be a pattern/expression)allPermissions?: string[]
- User needs ALL of these (each can be a pattern/expression)pattern?: string
- Legacy wildcard pattern (use permission
prop instead)anyPatterns?: string[]
- User needs ANY pattern matchallPatterns?: string[]
- User needs ALL pattern matchesfallback?: ReactNode
- Rendered when permission deniedrequireAuth?: boolean
- Require authentication (default: true)Returns:
userPermissions: string[]
- Userβs permissionshasPermission(permission: string): boolean
- Check permission (supports all features)hasAnyPermission(permissions: string[]): boolean
- Check any permissionhasAllPermissions(permissions: string[]): boolean
- Check all permissionshasPermissionPattern(pattern: string): boolean
- Advanced pattern checkinghasAnyPattern(patterns: string[]): boolean
- Check any pattern matcheshasAllPatterns(patterns: string[]): boolean
- Check all patterns matchgetMatchingPermissions(pattern: string): string[]
- Get permissions matching patterncheckExpression(expression: string): boolean
- Evaluate complex boolean expressionsisValidExpression(expression: string): boolean
- Validate expression syntaxisAuthenticated: boolean
- User authentication statuspermissions/
βββ index.ts # Main exports
βββ components/
β βββ can.tsx # Main Can component
β βββ with-permission.tsx # HOC wrapper
βββ hooks/
β βββ use-permissions.tsx # Main permissions hook
βββ types/
βββ index.ts # TypeScript definitions
permissions
folder<Can>
instead of manual permission checksCan
β @can
)resource.action
)<Can>
Component Interfaceinterface CanProps {
permission: string | boolean; // Permission to check
permissions?: string[]; // Optional custom permissions array
fallback?: React.ReactNode; // Content to render when access denied
children: React.ReactNode; // Content to render when access granted
}
Examples:
// Using auth permissions (default)
<Can permission="users.create">
<CreateButton />
</Can>
// Using custom permissions array
<Can
permission="admin.access"
permissions={['admin.access', 'super.user']}
>
<AdminPanel />
</Can>
// With fallback
<Can
permission="posts.view"
fallback={<div>No access</div>}
>
<PostList />
</Can>
usePermissions
Hook Interfacefunction usePermissions(permissions?: string[]): {
hasPermission: (permission: string | boolean) => boolean;
permissions: string[];
};
Parameters:
permissions
(optional): Array of permissions to use instead of auth permissionsReturns:
hasPermission
: Function to check if user has a specific permissionpermissions
: Current permissions array being usedExamples:
// Using auth permissions
const { hasPermission, permissions } = usePermissions();
// Using custom permissions
const { hasPermission } = usePermissions(['custom.perm', 'another.perm']);
// Check permissions
const canEdit = hasPermission('posts.edit');
const hasAny = hasPermission('admin.* || moderator.*');
withPermission
HOC Interfacefunction withPermission<T extends object>(
WrappedComponent: React.ComponentType<T>,
requiredPermission: string | boolean,
fallback?: React.ReactNode,
permissions?: string[]
): React.ComponentType<T>;
Parameters:
WrappedComponent
: Component to wraprequiredPermission
: Permission required to render componentfallback
(optional): What to render when access deniedpermissions
(optional): Custom permissions arrayExamples:
// Basic HOC usage
const ProtectedUserList = withPermission(UserList, 'users.view', <div>No access to users</div>);
// With custom permissions
const ProtectedFeature = withPermission(
FeatureComponent,
'feature.beta',
<div>Feature not available</div>,
['feature.beta', 'admin.override']
);
*
)<Can permission="users.*"> {/* users.create, users.edit, users.delete */}
<Can permission="admin.*"> {/* admin.users, admin.settings, admin.logs */}
<Can permission="*.create"> {/* users.create, posts.create, etc. */}
?
)<Can permission="user?.edit"> {/* user1.edit, user2.edit, userA.edit */}
<Can permission="level?.access"> {/* level1.access, level2.access, etc. */}
<Can permission="users.edit || posts.edit"> {/* OR logic */}
<Can permission="admin.access && users.manage"> {/* AND logic */}
<Can permission="(users.* || posts.*) && active"> {/* Grouped expressions */}
<Can permission="true"> {/* Always allow */}
<Can permission="false"> {/* Always deny */}
function TenantDashboard() {
const [tenantPermissions, setTenantPermissions] = useState([]);
useEffect(() => {
fetchTenantPermissions().then(setTenantPermissions);
}, []);
return (
<Can permission="tenant.admin || tenant.manager" permissions={tenantPermissions}>
<TenantControls />
</Can>
);
}
const featureFlags = ['feature.newUi', 'feature.advancedSearch'];
<Can permission="feature.newUi" permissions={featureFlags}>
<NewUserInterface />
</Can>;
<Can permission="(department.hr && level.manager) || admin.override">
<SensitiveEmployeeData />
</Can>
Full TypeScript support with proper type definitions:
import type { CanProps, UsePermissionsReturn } from '@devwizard/laravel-react-permissions';
const MyComponent: React.FC<CanProps> = ({ permission, children }) => {
const { hasPermission }: UsePermissionsReturn = usePermissions();
return <Can permission={permission}>{children}</Can>;
};
# Only import what you need - tree shaking works perfectly
import { Can } from '@devwizard/laravel-react-permissions'; # ~3kB
import { usePermissions } from '@devwizard/laravel-react-permissions'; # ~2kB
import { withPermission } from '@devwizard/laravel-react-permissions'; # ~1kB
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature
)git commit -m 'Add some amazing feature'
)git push origin feature/amazing-feature
)MIT License - see the LICENSE file for details.
IQBAL HASAN
@can
directive inspirationMade with β€οΈ by DevWizard