DevTools
The react-access-engine-devtools package provides a real-time debugging overlay for inspecting access checks, feature evaluations, policy traces, experiments, and user state.
Installation
# npm
npm install -D react-access-engine-devtools
# pnpm
pnpm add -D react-access-engine-devtools
# yarn
yarn add -D react-access-engine-devtoolsSetup
Add the DevTools component inside your <AccessProvider> and enable debug: true in your config:
import { AccessProvider, defineAccess } from "react-access-engine";
import { AccessDevtools } from "react-access-engine-devtools";
const config = defineAccess({
roles: ["admin", "editor", "viewer"] as const,
permissions: {
admin: ["*"] as const,
editor: ["articles:read", "articles:write"] as const,
viewer: ["articles:read"] as const,
},
features: {
"dark-mode": { enabled: true },
"new-editor": { rolloutPercentage: 50 },
},
debug: true, // Required for event recording
});
function App() {
return (
<AccessProvider config={config} user={user}>
<YourApp />
{/* Only renders in development — zero cost in production */}
<AccessDevtools />
</AccessProvider>
);
}DevTools Panels
The overlay includes 6 tabbed panels:
| Tab | What it shows |
|---|---|
| Overview | User ID, roles, permissions, plan, feature/experiment counts |
| Access | Permission checks with granted/denied status and reasons |
| Features | All feature flags with enabled/disabled state and reason codes |
| Policies | Policy evaluations with matched rules and effect |
| Experiments | Active experiments with assigned variants |
| Event Log | Combined chronological log of all events |
Filtering
Every tab (except Overview) has a filter bar. Type to search by permission name, feature name, or reason code. The Event Log tab also has type filters (All / Access / Feature / Policy).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
position | 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'bottom-right' | Panel position |
defaultOpen | boolean | false | Start with panel open |
shortcut | string | — | Reserved for custom shortcut (future) |
disableShortcut | boolean | false | Disable keyboard toggle |
Keyboard Shortcut
Toggle the panel with Ctrl+Shift+A (or Cmd+Shift+A on Mac).
// Disable the shortcut
<AccessDevtools disableShortcut />Positioning
Control where the overlay appears on screen:
<AccessDevtools position="bottom-right" /> {/* default */}
<AccessDevtools position="bottom-left" />
<AccessDevtools position="top-right" />
<AccessDevtools position="top-left" />Component Labels
Use DebugLabel to tag sections of your component tree. This makes devtools log entries easier to trace back to specific UI areas:
import { DebugLabel } from "react-access-engine-devtools";
function Sidebar() {
return (
<DebugLabel name="Sidebar">
<Can perform="settings:view">
<SettingsLink />
</Can>
<Can perform="admin:access">
<AdminLink />
</Can>
</DebugLabel>
);
}Programmatic Control
Toggle debug event collection at runtime without unmounting the overlay:
import {
enableDebug,
disableDebug,
isDebugEnabled,
} from "react-access-engine-devtools";
disableDebug(); // pause event collection
enableDebug(); // resume event collection
isDebugEnabled(); // check if collection is activeDebug Engine (Programmatic)
For programmatic access to debug information from the core package:
import { useAccessDebug } from "react-access-engine";
function MyDebugPanel() {
const {
lastChecks,
lastFeatureEvals,
lastPolicyEvals,
configSnapshot,
timestamp,
} = useAccessDebug();
return <pre>{JSON.stringify(lastChecks, null, 2)}</pre>;
}Debug Engine API
import { DebugEngine } from "react-access-engine";
const engine = new DebugEngine();
engine.setConfig(config);
// Record events
engine.recordAccessCheck(event);
engine.recordFeatureEval(event);
engine.recordPolicyEval(event);
// Get all debug info
const info = engine.getDebugInfo();
// Subscribe to real-time events
const unsubscribe = engine.subscribe((event) => {
console.log("Debug event:", event);
});
// Clear recorded events
engine.clear();The debug engine keeps a maximum of 100 entries per type (FIFO), so it won't cause memory leaks in long-running applications.
Architecture
┌─────────────────────────────────────────────┐
│ Your App │
│ ┌────────────────────────────────────────┐ │
│ │ <AccessProvider config={...} user={…}> │ │
│ │ ┌──────────────┐ │ │
│ │ │ DebugEngine │◄─── records events │ │
│ │ │ PluginEngine │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ subscribe() │ │
│ │ ┌──────▼───────────────────┐ │ │
│ │ │ <AccessDevtools /> │ │ │
│ │ │ ├─ useDevtoolsContext() │ │ │
│ │ │ ├─ useDebugLog() │ │ │
│ │ │ ├─ useDevtoolsSnapshot() │ │ │
│ │ │ └─ Panel UI │ │ │
│ │ └──────────────────────────┘ │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘Production Considerations
- Only include
<AccessDevtools>in development builds - The DevTools package is separate from the core, so it's tree-shaken out of production if you conditionally import it
- Setting
debug: false(or omitting it) disables event recording entirely AccessDevtoolsrendersnullin production — zero cost