Examples

Real-world patterns and recipes for using react-access-engine in production applications.

SaaS Dashboard with Plan Gating

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

const config = defineAccess({
  roles: ["admin", "member", "viewer"] as const,
  permissions: {
    admin: ["*"],
    member: ["projects:read", "projects:write", "billing:read"],
    viewer: ["projects:read"],
  },
  features: {
    aiAssistant: { enabled: true, allowedPlans: ["pro", "enterprise"] },
    customReports: { enabled: true, allowedPlans: ["enterprise"] },
    darkMode: { enabled: true },
  },
  plans: ["free", "pro", "enterprise"] as const,
});

function Dashboard() {
  return (
    <AccessProvider config={config} user={currentUser}>
      {/* Sidebar: admin-only settings */}
      <Can permission="billing:read">
        <BillingLink />
      </Can>

      {/* Plan-gated feature */}
      <AccessGate feature="aiAssistant" plan="pro">
        <AIAssistantWidget />
      </AccessGate>

      {/* Universal feature */}
      <Feature name="darkMode">
        <DarkModeToggle />
      </Feature>
    </AccessProvider>
  );
}

Multi-Tenant Application

const config = defineAccess({
  roles: ["org-admin", "org-member", "org-guest"] as const,
  permissions: {
    "org-admin": ["settings:*", "members:*", "projects:*"],
    "org-member": ["projects:read", "projects:write"],
    "org-guest": ["projects:read"],
  },
  policies: [
    {
      id: "same-org-only",
      effect: "allow",
      permissions: ["projects:read", "projects:write"],
      condition: (ctx) => ctx.user.attributes?.orgId === ctx.resource?.orgId,
      priority: 10,
    },
    {
      id: "owner-full-access",
      effect: "allow",
      permissions: ["projects:delete", "projects:transfer"],
      condition: (ctx) => ctx.resource?.ownerId === ctx.user.id,
      priority: 20,
    },
  ],
});

function ProjectList({ projects }) {
  return projects.map((project) => (
    <Can
      key={project.id}
      permission="projects:read"
      on={{ orgId: project.orgId }}
    >
      <ProjectCard project={project} />
    </Can>
  ));
}

A/B Testing with Analytics

import { createAnalyticsPlugin, defineAccess } from "react-access-engine";

const analytics = createAnalyticsPlugin({
  adapter: {
    track: (event, props) => mixpanel.track(event, props),
  },
  trackExperiments: true,
  trackFeatures: true,
});

const config = defineAccess({
  roles: ["user"] as const,
  permissions: { user: ["read"] },
  experiments: {
    "checkout-v2": {
      active: true,
      variants: ["control", "streamlined"],
      defaultVariant: "control",
      allocation: { control: 50, streamlined: 50 },
    },
    "onboarding-flow": {
      active: true,
      variants: ["classic", "guided", "minimal"],
      defaultVariant: "classic",
      allocation: { classic: 33, guided: 34, minimal: 33 },
    },
  },
  plugins: [analytics],
});

function CheckoutPage() {
  const { variant, active } = useExperiment("checkout-v2");

  return (
    <Experiment
      id="checkout-v2"
      variants={{
        control: <ClassicCheckout />,
        streamlined: <StreamlinedCheckout />,
      }}
      fallback={<ClassicCheckout />}
    />
  );
}

Next.js App Router Integration

// app/layout.tsx
import { AccessProvider } from "react-access-engine";
import { getUser } from "@/lib/auth";
import { accessConfig } from "@/lib/access-config";

export default async function RootLayout({ children }) {
  const user = await getUser();

  return (
    <html>
      <body>
        <AccessProvider config={accessConfig} user={user}>
          {children}
        </AccessProvider>
      </body>
    </html>
  );
}

// app/admin/page.tsx
("use client");
import { PermissionGuard } from "react-access-engine";

export default function AdminPage() {
  return (
    <PermissionGuard
      permissions={["admin:access"]}
      fallback={<p>You don't have admin access.</p>}
    >
      <AdminDashboard />
    </PermissionGuard>
  );
}

Feature Flags with Remote Config

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

const baseConfig = defineAccess({
  roles: ["user"] as const,
  permissions: { user: ["read"] },
  features: {
    maintenance: { enabled: false },
    newUI: { enabled: false },
  },
});

function App({ user }) {
  const { config, loading, stale } = useRemoteConfig(baseConfig, {
    url: "/api/feature-flags",
    pollInterval: 30_000,
  });

  return (
    <AccessProvider config={config} user={user}>
      <Feature name="maintenance" fallback={<MainApp />}>
        <MaintenancePage />
      </Feature>
    </AccessProvider>
  );
}

Custom Plugin for Rate Limiting

const rateLimitPlugin = {
  name: "rate-limiter",
  onAccessCheck: ({ permission, user, allowed }) => {
    if (!allowed) return;

    // Track access frequency per user
    const key = `${user.id}:${permission}`;
    const count = incrementCounter(key);

    if (count > 100) {
      logWarning(`High access frequency: ${key} (${count} checks)`);
    }
  },
};