Skip to content
React package

Laravel React Permissions

Show or hide React UI based on the permissions your Laravel backend shares through Inertia, with wildcards and boolean expressions.

pnpm add @devwizard/laravel-react-permissions
View on GitHub
On this page

Overview

Laravel React Permissions brings the idea of Blade’s @can to React and Inertia. Your Laravel app shares the signed-in user’s permission names as an Inertia prop; a Can component, a withPermission wrapper and a usePermissions hook then decide what to render.

Permission checks support exact names, wildcards such as users.*, and boolean expressions such as (users.* || posts.*) && admin.access. The package has no runtime dependencies beyond React and Inertia, and is MIT licensed and maintained by the DevWizard core team.

These checks shape the interface only. Always enforce permissions on the server as well.

Features

  • Can component for conditional rendering with a fallback.
  • withPermission higher-order component that gates a whole component.
  • usePermissions hook with exact, any, all, pattern and expression checks.
  • Wildcards: * matches any run of characters and ? matches one.
  • Boolean expressions with ||, &&, |, &, parentheses and the literals true and false.
  • Override the permission list per call with a permissions array.
  • Authentication requirement, on by default.
  • Exported types for the shared props, hook return and component props.
  • Tree-shakeable ESM build; works with the permission names produced by packages such as Spatie Laravel Permission.

Requirements

  • React 18 or 19.
  • @inertiajs/react 1 or 2.
  • A Laravel backend that shares auth.user.permissions as an array of strings.

Installation

Terminal
pnpm add @devwizard/laravel-react-permissions

Other package managers:

Terminal
npm install @devwizard/laravel-react-permissions
yarn add @devwizard/laravel-react-permissions

Configuration

There is no provider and no config file. Share the permissions from your Inertia middleware, under auth.user.permissions:

PHP
public function share(Request $request): array
{
    return [
        ...parent::share($request),
        'auth' => [
            'user' => $request->user() ? [
                'id' => $request->user()->id,
                'name' => $request->user()->name,
                'permissions' => $request->user()->getAllPermissions()->pluck('name')->toArray(),
            ] : null,
        ],
    ];
}

getAllPermissions() comes from Spatie Laravel Permission; any source that produces an array of permission names works.

Usage

Wrap UI in Can:

TSX
import { Can } from '@devwizard/laravel-react-permissions';

export function Toolbar() {
  return (
    <Can permission="users.create" fallback={<span>No access</span>}>
      <button>Create user</button>
    </Can>
  );
}

Read the same checks in code with the hook:

TSX
import { usePermissions } from '@devwizard/laravel-react-permissions';

export function Menu() {
  const { hasPermission, isAuthenticated } = usePermissions();

  if (!isAuthenticated) return null;

  return hasPermission('reports.*') ? <ReportsLink /> : null;
}

Gate a whole component:

TSX
import { withPermission } from '@devwizard/laravel-react-permissions';

export const AdminPanel = withPermission(Panel, {
  expression: '(admin.* || manager.*) && active.user',
  fallback: <p>Admin access required</p>,
});

API Reference

Can props

When several checking props are given, the first one present wins, in this order: expression, permission, anyPermissions, allPermissions, anyPatterns, allPatterns, pattern.

Prop Type Description
permission string Exact name, wildcard or expression
expression string Boolean expression
anyPermissions string[] Passes if any entry matches exactly
allPermissions string[] Passes if every entry matches exactly
anyPatterns string[] Passes if any pattern or expression passes
allPatterns string[] Passes if every pattern or expression passes
pattern string Single pattern, kept for older code
permissions string[] Use this list instead of the shared one; an empty array means none
fallback ReactNode Rendered when access is denied
requireAuth boolean Deny unauthenticated users; default true
children ReactNode Rendered when access is granted

With requireAuth set to false and no checking prop, Can renders its children.

withPermission(Component, options)

Takes the same options as Can, except children, and returns a wrapped component with a withPermission(Name) display name.

usePermissions(permissions?)

Member Description
userPermissions The active list: the argument if given, else auth.user.permissions, else empty
hasPermission(permission) Exact name, or a pattern or expression when it contains wildcards or operators
hasAnyPermission(list) True if any entry is in the list; exact names only
hasAllPermissions(list) True if every entry is in the list; exact names only
hasPermissionPattern(pattern) Pattern or expression check
hasAnyPattern(patterns) Any pattern passes
hasAllPatterns(patterns) All patterns pass
getMatchingPermissions(pattern) The user’s permissions that match a wildcard pattern
checkExpression(expression) Evaluate a boolean expression
isValidExpression(expression) Whether an expression is syntactically valid
isAuthenticated True when auth.user exists

Types

Auth, SharedData, User, NavItem, PermissionProps, CanProps, UsePermissionsReturn and WithPermissionOptions.

Examples

Show an action if the user can do anything with posts, or manage the site:

TSX
<Can permission="posts.* || site.manage">
  <NewPostButton />
</Can>

Filter a navigation list:

TSX
const { hasPermission } = usePermissions();
const visible = items.filter((item) => !item.permission || hasPermission(item.permission));

List the permissions behind a wildcard:

TSX
const { getMatchingPermissions } = usePermissions();

getMatchingPermissions('users.*');

Test a component with a fixed permission list:

TSX
<Can permission="admin.access" permissions={['admin.access']}>
  <AdminLink />
</Can>

FAQ & Troubleshooting

Everything is hidden. Check that the page props contain auth.user.permissions as an array of strings. Can denies by default for signed-out users while requireAuth is on.

anyPermissions ignores my wildcard. It compares exact names. Use anyPatterns, or put the alternatives in one permission expression with ||.

Is this secure? No client-side check is. The permission list is visible in the page data, and hiding a button does not protect an endpoint. Authorize every request in Laravel with policies or middleware.

My expression always returns false. Use isValidExpression to test it, and check for a typo in a permission name. Invalid expressions log a warning and evaluate to false.

Can I use permissions from somewhere other than Inertia? Yes. Pass permissions to Can or to usePermissions and the shared list is ignored. The hook still calls Inertia’s usePage, so use it under an Inertia app.

Changelog & Support

Release history is in the CHANGELOG and on the GitHub releases page. Report bugs and request features in GitHub issues. Source: DevWizardHQ/laravel-react-permissions. For help with Laravel and React products, the DevWizard team takes project contracts and monthly retainers through the contact page.