Introduction

react-access-engine is a unified, type-safe access control library for React that combines RBAC, ABAC, policy engine, feature flags, A/B experiments, plan gating, remote config, and a plugin system into a single tree-shakeable package.

Why react-access-engine?

Modern applications need multiple layers of access control:

  • Who can see this? → Role-based access (RBAC)
  • Under what conditions? → Attribute-based policies (ABAC)
  • Is this feature live? → Feature flags
  • Which variant? → A/B experiments
  • What plan are they on? → Subscription gating
  • Should we refresh rules? → Remote config

Most teams combine 3–4 separate libraries or services for this. react-access-engine replaces them all with a single <AccessProvider>, a single config object, and a unified set of hooks and components.

Key Features

  • RBAC — Roles, permissions, wildcard matching (posts:*, *)
  • ABAC — Declarative condition-based policy rules with custom operators
  • Feature Flags — Static, role-gated, plan-gated, environment-scoped, with rollouts and dependencies
  • Experiments — Deterministic SSR-safe A/B assignment with allocation control
  • Plan Gating — Hierarchical subscription tiers with automatic comparison
  • Remote Config — Fetch and merge config from an API with stale-while-revalidate
  • Plugin System — Hooks for audit logging, analytics, custom operators
  • DevTools — Real-time overlay for debugging access checks, features, policies
  • Type-Safe — Full TypeScript inference with InferRoles, InferPermissions, etc.
  • SSR-Ready — Works with Next.js App Router (all components are 'use client')
  • Tree-Shakeable — Import only what you use — zero dependencies

Quick Example

import {
  defineAccess,
  AccessProvider,
  Can,
  Feature,
} from "react-access-engine";

const config = defineAccess({
  roles: ["admin", "editor", "viewer"] as const,
  permissions: {
    admin: ["*"],
    editor: ["posts:read", "posts:write"],
    viewer: ["posts:read"],
  },
  features: {
    newEditor: { enabled: true, allowedRoles: ["admin"] },
  },
});

const user = { id: "user-1", roles: ["editor"] as const };

function App() {
  return (
    <AccessProvider config={config} user={user}>
      <Can permission="posts:write">
        <button>Edit Post</button>
      </Can>
      <Feature name="newEditor" fallback={<ClassicEditor />}>
        <NewEditor />
      </Feature>
    </AccessProvider>
  );
}

Next Steps