laravel-react-permissions

@devwizard/laravel-react-permissions

[![npm version](https://img.shields.io/npm/v/@devwizard/laravel-react-permissions.svg)](https://www.npmjs.com/package/@devwizard/laravel-react-permissions) [![npm downloads](https://img.shields.io/npm/dt/@devwizard/laravel-react-permissions.svg)](https://www.npmjs.com/package/@devwizard/laravel-react-permissions) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/) [![Tree Shakeable](https://img.shields.io/badge/Tree%20Shakeable-βœ“-brightgreen.svg)](#) [![Zero Dependencies](https://img.shields.io/badge/Dependencies-Zero-brightgreen.svg)](#) **πŸ” Modern, Laravel-inspired permissions system for React/Inertia.js** _Advanced pattern matching β€’ Boolean expressions β€’ Zero dependencies β€’ Full TypeScript support_ [πŸ“– Documentation](#-installation) β€’ [πŸš€ Quick Start](#️-quick-setup) β€’ [πŸ’‘ Examples](#-advanced-use-cases) β€’ [🎯 Patterns](#-pattern-matching)

🌟 Why Choose This Package?

πŸš€ Features

πŸ“¦ Installation

npm install @devwizard/laravel-react-permissions
# or
yarn add @devwizard/laravel-react-permissions
# or
pnpm add @devwizard/laravel-react-permissions

πŸ—οΈ Quick Setup

1. Laravel Backend Setup

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,
        ],
    ];
}

2. React Frontend Usage

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>
  );
}

🎯 Core Components

<Can> Component

React component for conditional rendering based on permissions:

<Can permission="users.* || admin.access" fallback={<div>Access denied</div>}>
  <UserManagement />
</Can>

Props:

usePermissions Hook

Hook 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:

Returns:

withPermission HOC

Higher-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']
);

πŸ” Pattern Matching

Wildcards (*) - 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. */}

Single Characters (?) - Match One Character

<Can permission="user?.edit">     {/* user1.edit, user2.edit, userA.edit */}
<Can permission="level?.access">  {/* level1.access, level2.access, etc. */}

Boolean Logic - Logical Operators

<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 */}

Complex Expressions

{
  /* 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>;

🎨 Advanced Use Cases

Dynamic Permissions from API

function TenantDashboard() {
  const [tenantPermissions, setTenantPermissions] = useState([]);

  useEffect(() => {
    fetchTenantPermissions().then(setTenantPermissions);
  }, []);

  return (
    <Can permission="tenant.admin || tenant.manager" permissions={tenantPermissions}>
      <TenantControls />
    </Can>
  );
}

Feature Flags

const featureFlags = ['feature.newUi', 'feature.advancedSearch'];

<Can permission="feature.newUi" permissions={featureFlags}>
  <NewUserInterface />
</Can>;

Testing Scenarios

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

Custom Permission Sources

// 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>;

πŸ”§ TypeScript Support

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>;
};

πŸ§ͺ Compatibility

πŸ“Š Performance

πŸ›‘οΈ Security

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

MIT License - see the LICENSE file for details.

πŸ‘¨β€πŸ’» Author

IQBAL HASAN

πŸ™ Acknowledgments


Made with ❀️ by DevWizard

πŸ—οΈ Quick Setup

1. Laravel Backend Setup

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,
        ],
    ];
}

2. React Frontend Usage

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>
  );
}

πŸ” Advanced Pattern Matching & Boolean Logic

The permissions system supports powerful pattern matching and boolean expressions for maximum flexibility:

Boolean Values

Built-in Boolean Support

<Can permission="true">      {/* Always visible to authenticated users */}
<Can permission="false">     {/* Never visible (useful for testing) */}

Logical Operators

1. OR Operators (|| and |)

<Can permission="users.* || posts.*">        {/* User management OR post management */}
<Can permission="admin.access | manager.access">  {/* Admin OR manager access */}

2. AND Operators (&& and &)

<Can permission="users.* && admin.access">   {/* User permissions AND admin access */}
<Can permission="reports.view & security.clearance">  {/* Reports AND security clearance */}

3. Complex Expressions with Parentheses

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

Custom Permissions Support

Instead of always using auth permissions, you can provide your own permissions array:

Using Custom Permissions

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

Hook with Custom Permissions

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)

Common Use Cases

Pattern Types

1. Wildcard (*) - Match Any Characters

<Can permission="users.*">     {/* matches: users.create, users.edit, users.delete */}
<Can permission="admin.*">     {/* matches: admin.users, admin.settings, admin.reports */}

2. Single Character (?) - 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 */}

3. Exact Match - No Wildcards

<Can permission="users.create">  {/* exact match only */}

Advanced Usage Examples

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

Expression Validation

const { isValidExpression, checkExpression } = usePermissions();

// Validate expressions before using them
const isValid = isValidExpression('users.* && (admin.access || manager.role)');
const result = checkExpression('(admin.* && security.*) || system.override');

πŸ“‹ API Reference

Installation

  1. Copy this entire permissions folder to your resources/js/ directory
  2. Update your Laravel middleware to pass permissions via Inertia props
  3. Import and use the components

Laravel Backend Setup

Add 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,
        ],
    ];
}

Usage

Component Usage

The <Can> component follows Laravel’s @can convention and supports:

Basic Permission Check

import { Can } from '@/permissions';

<Can permission="users.create">
  <button>Create User</button>
</Can>;

Using the Hook

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.*',
  },
];

Higher-Order Component

import { withPermission } from '@/permissions';

const ProtectedComponent = withPermission(MyComponent, {
  permission: 'admin.access',
  fallback: <div>Access denied</div>,
});

Component API

Can Component

Props:

usePermissions Hook

Returns:

File Structure

permissions/
β”œβ”€β”€ 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

Integration with Existing Projects

  1. Copy the entire permissions folder
  2. Update import paths if your project structure differs
  3. Ensure your Laravel backend passes permissions via Inertia props
  4. Update your existing components to use <Can> instead of manual permission checks

Laravel Conventions

πŸ“š Detailed API Reference

<Can> Component Interface

interface 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 Interface

function usePermissions(permissions?: string[]): {
  hasPermission: (permission: string | boolean) => boolean;
  permissions: string[];
};

Parameters:

Returns:

Examples:

// 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 Interface

function withPermission<T extends object>(
  WrappedComponent: React.ComponentType<T>,
  requiredPermission: string | boolean,
  fallback?: React.ReactNode,
  permissions?: string[]
): React.ComponentType<T>;

Parameters:

Examples:

// 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']
);

πŸ” Pattern Matching

Wildcards (*)

<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. */}

Single Characters (?)

<Can permission="user?.edit">     {/* user1.edit, user2.edit, userA.edit */}
<Can permission="level?.access">  {/* level1.access, level2.access, etc. */}

Boolean Logic

<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 */}

🎨 Advanced Examples

Dynamic Permissions from API

function TenantDashboard() {
  const [tenantPermissions, setTenantPermissions] = useState([]);

  useEffect(() => {
    fetchTenantPermissions().then(setTenantPermissions);
  }, []);

  return (
    <Can permission="tenant.admin || tenant.manager" permissions={tenantPermissions}>
      <TenantControls />
    </Can>
  );
}

Feature Flags

const featureFlags = ['feature.newUi', 'feature.advancedSearch'];

<Can permission="feature.newUi" permissions={featureFlags}>
  <NewUserInterface />
</Can>;

Complex Business Logic

<Can permission="(department.hr && level.manager) || admin.override">
  <SensitiveEmployeeData />
</Can>

πŸ“š Documentation

πŸ”§ TypeScript Support

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>;
};

πŸ“Š Performance & Bundle Size

Bundle Analysis

Bundle Impact

# 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

Browser Support

πŸ§ͺ Compatibility

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

MIT License - see the LICENSE file for details.

πŸ‘¨β€πŸ’» Author

IQBAL HASAN

πŸ™ Acknowledgments

πŸ“Š Stats


Made with ❀️ by DevWizard