Core Concepts
Architecture Overview
react-access-engine is built around a single <AccessProvider> that initializes several specialized engines:
┌─────────────────────────────────────────────────────┐
│ AccessProvider │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Role │ │Permission│ │ Feature │ │
│ │ Engine │ │ Engine │ │ Engine │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Policy │ │ Plan │ │Experiment│ │
│ │ Engine │ │ Engine │ │ Engine │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Plugin │ │ Debug │ │
│ │ Engine │ │ Engine │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────┘Config Object
All access rules are defined in a single config object using defineAccess():
const config = defineAccess({
roles: [...], // Role definitions
permissions: {...}, // Role → permission mapping
features: {...}, // Feature flag definitions
experiments: {...}, // A/B experiment definitions
policies: [...], // ABAC policy rules
plans: [...], // Plan hierarchy (low → high)
environment: {...}, // Environment context
plugins: [...], // Plugin instances
debug: true, // Enable debug engine
});User Context
The user object describes the current authenticated user:
const user = {
id: "user-42", // Required: unique identifier
roles: ["editor"] as const, // Required: assigned roles
plan: "pro", // Optional: subscription plan
attributes: {
// Optional: ABAC attributes
department: "engineering",
country: "US",
joinedAt: "2023-01-15",
},
};Permission Resolution
Permissions are resolved in this order:
- RBAC Check — Does the user's role grant this permission?
- Exact match:
posts:writematchesposts:write - Namespace wildcard:
posts:*matchesposts:write - Global wildcard:
*matches everything
- Exact match:
- Policy Check — If ABAC policies exist, do they allow it?
- Policies can override RBAC (a policy
denyblocks even if RBAC allows) - Policy rules are sorted by priority (highest first), first match wins
- Default: deny if policies exist but none match
- Policies can override RBAC (a policy
Feature Evaluation
Features are evaluated with this precedence:
- Static —
enabled: true/false - Environment —
allowedEnvironments: ['production'] - Role —
allowedRoles: ['admin', 'beta-tester'] - Plan —
allowedPlans: ['pro', 'enterprise'] - Dependencies —
dependencies: ['otherFeature'](topologically resolved) - Rollout —
rolloutPercentage: 25(deterministic hash, SSR-safe)
If a feature has dependencies, they're resolved via topological sort. Circular dependencies result in the feature being disabled.
Hooks vs Components
react-access-engine provides both declarative components and imperative hooks:
| Use Case | Component | Hook |
|---|---|---|
| Show/hide UI by permission | <Can> | usePermission() |
| Feature flag gate | <Feature> | useFeature() |
| Multi-condition gate | <AccessGate> | useAccess() |
| Route protection | <PermissionGuard> | usePermission() |
| A/B variant rendering | <Experiment> | useExperiment() |
| Plan-gated UI | <AccessGate plan="pro"> | usePlan() |
Use components for simple show/hide logic. Use hooks when you need programmatic control (e.g., conditional API calls, analytics, styling).
Config Merging
Use mergeConfigs() to combine base configs with overrides:
import { defineAccess, mergeConfigs } from "react-access-engine";
const baseConfig = defineAccess({
roles: ["admin", "user"] as const,
permissions: { admin: ["*"], user: ["read"] },
});
const withFeatures = mergeConfigs(baseConfig, {
features: { darkMode: { enabled: true } },
});This is useful for layering environment-specific config on top of a base config, or for remote config merging.
Next Steps
- API Reference — Full hook and component API
- Policies & ABAC — Attribute-based access control
- Feature Flags — Advanced feature configuration