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

Setup

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:

TabWhat it shows
OverviewUser ID, roles, permissions, plan, feature/experiment counts
AccessPermission checks with granted/denied status and reasons
FeaturesAll feature flags with enabled/disabled state and reason codes
PoliciesPolicy evaluations with matched rules and effect
ExperimentsActive experiments with assigned variants
Event LogCombined 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

PropTypeDefaultDescription
position'bottom-right' | 'bottom-left' | 'top-right' | 'top-left''bottom-right'Panel position
defaultOpenbooleanfalseStart with panel open
shortcutstringReserved for custom shortcut (future)
disableShortcutbooleanfalseDisable 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 active

Debug 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
  • AccessDevtools renders null in production — zero cost