npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

permissionforge-react

v1.2.0

Published

Custom React and TypeScript RBAC SDK with role, permission, menu, route, field, action, and audit guards for admin panels.

Readme

permissionforge-react

React TypeScript License: MIT npm package

Production-ready role and permission based access control for React admin panels, CRM, ERP, BSG Backoffice, internal dashboards, and enterprise applications.

Built with React 18/19 compatibility, TypeScript, Vite library mode, ESM/CJS output, and tree-shakable exports.

Official website: https://bsgtechnologies.com - Visit here to meet, learn, contribute, discuss new topics, and connect with BSG Technologies.

Developer note: Pradeep Kumar Sheoran (Developer) is open to a job change. You can use his React.js, React Native, Android Java, Node.js packages, TypeScript scripts, and newly launched custom utilities. For feature requests or updates, leave a comment or connect through the official website.

Features

  • Show or hide UI with <Can />
  • Disable buttons with <DisableWhenNoAccess />
  • Protect pages with <RouteGuard />
  • Filter sidebars and nested menus
  • Guard sensitive sync or async actions
  • Support roles, permissions, any and all matching
  • Optional wildcard permissions such as user.*
  • Optional case-insensitive role and permission checks
  • Deny rules with deniedPermission and deniedRole
  • Permission-or-role rules with condition="or"
  • Permission groups for reusable bundles such as user.manage
  • Tenant, organization, ownership, and resource-based rules
  • Field-level permissions with <FieldGuard />
  • Custom policy callbacks with when
  • Audit logging through onAccessDenied
  • Debug-friendly decisions with getAccessDecision
  • Async permission loading state in AccessProvider
  • HOC support through withAccess
  • Render-prop support in <Can />
  • Nested route config filtering with filterRoutesByAccess
  • Typed permission builders with permission() and createPermissionMap()
  • Role hierarchy and permission aliases for enterprise migrations
  • Feature flag, environment, and time-window access rules
  • Policy presets: ownsResource, sameTenant, underApprovalLimit
  • Permission snapshots for debugging and support
  • Menu transform mode to disable denied items with reasons
  • Testing helpers and mock users through permissionforge-react/testing
  • Super admin override with user.isSuperAdmin or configured roles
  • Router-agnostic redirect fallback
  • Custom router navigation support
  • Non-React access controller for services and callbacks
  • Strong TypeScript autocomplete
  • Lightweight package with no runtime dependency beyond React

Install

npm install permissionforge-react
yarn add permissionforge-react
pnpm add permissionforge-react

Quick Start

import { AccessProvider, Can } from "permissionforge-react";

const user = {
  id: 1,
  name: "Admin User",
  roles: ["admin"],
  permissions: ["user.create", "user.edit", "dashboard.view"],
  isSuperAdmin: false
};

export function App() {
  return (
    <AccessProvider
      user={user}
      superAdminRoles={["superadmin", "root"]}
      roleHierarchy={{ admin: ["manager", "viewer"], manager: ["viewer"] }}
      defaultOptions={{ allowWildcard: true }}
    >
      <Can permission="user.create">
        <button>Create User</button>
      </Can>
    </AccessProvider>
  );
}

Access Rules

Rules can use permission, role, or both. When both are provided, both must pass.

<Can permission={["user.create", "user.edit"]} match="any">
  <button>Manage User</button>
</Can>

<Can permission={["user.create", "user.edit"]} match="all">
  <button>Full User Access</button>
</Can>

<Can role="admin">
  <section>Admin Panel</section>
</Can>

<Can permission="user.delete" fallback={<span>No Access</span>}>
  <button>Delete User</button>
</Can>

Advanced rules remain optional:

<Can permission="user.*">
  <button>Manage Users</button>
</Can>

<Can permission="reports.view" deniedRole="suspended">
  <Reports />
</Can>

<Can permission="billing.view" role="finance-admin" condition="or">
  <Billing />
</Can>

Render-prop mode keeps layout visible while still exposing the decision:

<Can permission="user.create">
  {(allowed) => <button disabled={!allowed}>Create User</button>}
</Can>

Permission Groups

Create named bundles once and reuse them across components, routes, menus, and actions.

const permissionGroups = {
  "user.manage": ["user.create", "user.edit", "user.delete"],
  "invoice.manage": ["invoice.view", "invoice.approve"]
};

<AccessProvider user={user} permissionGroups={permissionGroups}>
  <Can permissionGroup="user.manage" match="all">
    <button>Manage Users</button>
  </Can>
</AccessProvider>;

Tenant, Ownership, and Resource Rules

Useful for SaaS, CRM, ERP, branch-based systems, and BSG-style backoffice apps.

const user = {
  id: 7,
  roles: ["manager"],
  permissions: ["invoice.approve"],
  tenantIds: ["delhi-branch"]
};

const invoice = {
  id: "INV-101",
  tenantId: "delhi-branch",
  ownerId: 7,
  amount: 75000
};

<Can
  permission="invoice.approve"
  resource={invoice}
  when={({ resource }) => Number(resource?.amount ?? 0) <= 100000}
>
  <button>Approve Invoice</button>
</Can>;

By default, resource checks use tenantId and ownerId. Use tenantKey and ownerKey when your API uses different field names.

For sensitive resources, require the metadata so a missing API field cannot silently allow access:

<Can permission="invoice.approve" resource={invoice} requireTenant requireOwner>
  <button>Approve Invoice</button>
</Can>

Enable strict configuration checks to deny unknown permission groups. You can also make explicit deny rules override super-admin access and evaluate day/hour rules in UTC:

<AccessProvider
  user={user}
  permissionGroups={permissionGroups}
  defaultOptions={{
    strict: true,
    superAdminMode: "respect-explicit-deny",
    timeZone: "utc"
  }}
>
  <App />
</AccessProvider>

Reliability behavior:

| Feature | Configuration | Result | | --- | --- | --- | | Unknown permission group | strict: true | Denied with UNKNOWN_PERMISSION_GROUP | | Required tenant metadata | requireTenant | Missing tenant denied with TENANT_MISSING | | Required owner metadata | requireOwner | Missing owner or user ID denied with OWNER_MISSING | | User-owned wildcard | allowWildcard: true | user.* satisfies user.create | | Alias and role cycles | Automatic | Resolves safely without infinite recursion | | Invalid time input | Automatic | Denied with INVALID_TIME_VALUE | | Deterministic time rules | timeZone: "utc" | UTC day/hour evaluation | | Thrown custom policy | Automatic | Denied with POLICY_ERROR | | Super-admin deny precedence | superAdminMode: "respect-explicit-deny" | Explicit deny is evaluated first |

Detailed setup and response handling are covered in the Implementation Guide, Customization Guide, and Response Guide.

Field-Level Permissions

import { FieldGuard } from "permissionforge-react";

<FieldGuard field="salary" fallback={<span>Hidden</span>}>
  <SalaryValue />
</FieldGuard>;

field="salary" checks salary.view.

Audit Logging and Debug Decisions

<AccessProvider
  user={user}
  onAccessDenied={({ user, source, decision }) => {
    console.warn("Denied", user?.id, source, decision.reasons);
  }}
>
  <Can permission="settings.update" source="settings-save-button">
    <button>Save Settings</button>
  </Can>
</AccessProvider>

Use getAccessDecision when you need reasons for logs, tests, or debugging:

import { getAccessDecision } from "permissionforge-react";

const decision = getAccessDecision(user, { permission: "settings.update" });
console.log(decision.allowed, decision.missingPermissions, decision.reasons);

Async Permission Loading

<AccessProvider user={user} loading={isLoading} loadingFallback={<Spinner />}>
  <App />
</AccessProvider>

Disable Button Instead of Hiding

import { DisableWhenNoAccess } from "permissionforge-react";

<DisableWhenNoAccess permission="user.delete">
  <button>Delete User</button>
</DisableWhenNoAccess>;

If the user is denied, the child element is cloned with disabled={true} and aria-disabled={true}. If you pass fallback, the fallback is rendered instead.

Hooks

import { useAccess, useCanAccess, usePermission, useRole } from "permissionforge-react";

function Toolbar() {
  const { user, isSuperAdmin, hasPermission, hasRole, canAccess } = useAccess();
  const canCreate = useCanAccess({ permission: "user.create" });
  const hasCreatePermission = usePermission("user.create");
  const isAdmin = useRole("admin");

  return canCreate ? <button>Create</button> : null;
}

Hooks must be used inside AccessProvider. If not, permissionforge-react throws a clear error.

Route Guard

import { RouteGuard } from "permissionforge-react";

<RouteGuard permission="dashboard.view" redirectTo="/unauthorized">
  <Dashboard />
</RouteGuard>;

RouteGuard does not require react-router-dom. When redirectTo is provided and access is denied, it uses window.location.assign(redirectTo). You can also pass a router-specific navigation function.

const navigate = useNavigate();

<RouteGuard permission="dashboard.view" redirectTo="/unauthorized" navigate={navigate}>
  <Dashboard />
</RouteGuard>;

You can also pass a router-specific unauthorized component through fallback.

For nested route config filtering:

import { filterRoutesByAccess } from "permissionforge-react";

const routes = [
  { path: "/dashboard", permission: "dashboard.view" },
  { path: "/settings", role: "superadmin" }
];

const allowedRoutes = filterRoutesByAccess(routes, user, {
  superAdminRoles: ["superadmin"]
});
<RouteGuard permission="reports.view" fallback={<Unauthorized />}>
  <Reports />
</RouteGuard>

Sidebar Menu Filtering

import { filterMenuByAccess } from "permissionforge-react";

const menu = [
  {
    label: "Dashboard",
    path: "/dashboard",
    permission: "dashboard.view"
  },
  {
    label: "Users",
    path: "/users",
    permission: "user.view",
    children: [
      {
        label: "Create User",
        path: "/users/create",
        permission: "user.create"
      }
    ]
  }
];

const filteredMenu = filterMenuByAccess(menu, user, ["superadmin"]);

Parent items are kept when they have accessible children, even if the parent rule itself fails.

Wildcard menu checks can be enabled per call:

const filteredMenu = filterMenuByAccess(menu, user, ["superadmin"], {
  allowWildcard: true
});

Action Guard

import { guardAction } from "permissionforge-react";

await guardAction({
  permission: "user.delete",
  user,
  action: () => deleteUser(id),
  onDenied: () => alert("You do not have permission")
});

BSG-branded alias and callback types are also exported:

import type { BsgAccessCallback, BsgDeniedCallback } from "permissionforge-react";
import { bsgGuardAction } from "permissionforge-react";

Non-React Usage

Use createAccessController inside API clients, callbacks, or service modules.

import { createAccessController } from "permissionforge-react";

const access = createAccessController(user, {
  superAdminRoles: ["superadmin"],
  allowWildcard: true
});

if (access.canAccess({ permission: "invoice.*" })) {
  await syncInvoices();
}

HOC Usage

import { withAccess } from "permissionforge-react";

const SecureDeleteButton = withAccess(DeleteButton, {
  permission: "user.delete",
  fallback: null,
  source: "delete-user-button"
});

BSG Backoffice Example

const bsgUser = {
  id: "BO-1024",
  name: "Operations Admin",
  roles: ["operations-admin"],
  permissions: [
    "dashboard.view",
    "customer.view",
    "customer.update",
    "payment.reconcile"
  ]
};

<AccessProvider user={bsgUser} superAdminRoles={["superadmin"]}>
  <Can permission="customer.update">
    <button>Update Customer</button>
  </Can>
</AccessProvider>;

More Advanced Features

Typed permission builders:

import { createPermissionMap, permission } from "permissionforge-react";

const permissions = createPermissionMap(["user.create", "user.edit"] as const);
const createUser = permission("user", "create");

Permission aliases and role hierarchy:

<AccessProvider
  user={user}
  permissionAliases={{ "user.add": "user.create" }}
  roleHierarchy={{ admin: ["manager"], manager: ["viewer"] }}
>
  <Can permission="user.add">Create</Can>
  <Can role="viewer">Inherited Role Content</Can>
</AccessProvider>

Alias chains and cyclic alias or role configurations are resolved safely without infinite recursion.

Feature flag, environment, and time-window rules:

<AccessProvider
  user={user}
  featureFlags={{ newBilling: true }}
  defaultOptions={{
    environment: "production"
  }}
>
  <Can
    permission="billing.view"
    feature="newBilling"
    environment="production"
    validFrom="2026-01-01"
    validUntil="2026-12-31"
    allowedDays={[1, 2, 3, 4, 5]}
  >
    <Billing />
  </Can>
</AccessProvider>

Policy presets:

import { ownsResource, sameTenant, underApprovalLimit } from "permissionforge-react";

<Can resource={invoice} when={ownsResource()}>
  <button>Edit Own Invoice</button>
</Can>

<Can resource={invoice} when={sameTenant()}>
  <button>Open Branch Invoice</button>
</Can>

<Can resource={invoice} when={underApprovalLimit(100000)}>
  <button>Approve</button>
</Can>

Reason and boundary components:

import { AccessBoundary, AccessReason } from "permissionforge-react";

<AccessBoundary permission="reports.view" fallback={<Unauthorized />}>
  <Reports />
</AccessBoundary>

<AccessReason permission="settings.update">
  {(reasons) => <p>{reasons.join(", ")}</p>}
</AccessReason>

Menu transform mode:

import { transformMenuByAccess } from "permissionforge-react";

const menuWithDisabledItems = transformMenuByAccess(menu, user, {
  deniedMode: "disable"
});

Snapshots:

import { createAccessSnapshot } from "permissionforge-react";

const snapshot = createAccessSnapshot(user, { permissionGroups });

Testing helpers:

import { mockUsers, renderWithAccess } from "permissionforge-react/testing";

renderWithAccess(<MyButton />, {
  user: mockUsers.admin
});

Available mock users: superadmin, admin, manager, viewer, denied.

Security reminder: permissionforge-react is a frontend UX guard. Always enforce authorization on the backend too.

Advanced v1.2 SDK

Permission Forge 1.2 adds an enterprise policy layer while keeping every existing v1.x API compatible.

import {
  createAccessCache,
  createPermissionForge,
  defineAccessConfig
} from "permissionforge-react/core";

const config = defineAccessConfig({
  version: "billing-2026.1",
  policies: {
    approveInvoice: {
      name: "approveInvoice",
      version: "billing-2026.1",
      priority: 100,
      effect: "allow",
      rule: {
        allOf: [
          { permission: "invoice.approve" },
          { role: ["manager", "finance-head"] },
          { not: { deniedRole: "suspended" } }
        ]
      }
    }
  }
});

const forge = createPermissionForge({
  ...config,
  permissions: ["invoice.view", "invoice.approve"] as const,
  roles: ["manager", "finance-head", "suspended"] as const,
  cache: createAccessCache({ ttlMs: 30000 })
});

forge.can(user, forge.policy("approveInvoice", "billing-2026.1"));

Advanced capabilities:

  • Recursive allOf, anyOf, oneOf, and not policies with cycle and depth protection.
  • Named, versioned, prioritized allow/deny policies and conflict resolution.
  • Typed SDK factory with permission, role, and feature autocomplete.
  • Async and batch decisions with concurrency, cancellation, timeout, and failure mode controls.
  • Immutable field masking and row-level resource filtering.
  • Bounded TTL decision cache that bypasses callback policies.
  • Decision IDs, correlation IDs, redacted evaluation traces, and custom audit adapters.
  • IP, device, location, session, tenant, owner, environment, feature, and time conditions.
  • Headless config validation and AccessInspector primitives for building a policy studio.
  • Dedicated core, node, react, and react-native package entrypoints.
import { canAccess } from "permissionforge-react/core";
import { createPermissionForge } from "permissionforge-react/node";
import { AccessProvider, Can } from "permissionforge-react/react";
import { AccessProvider as NativeAccessProvider } from "permissionforge-react/react-native";

See the Advanced SDK Guide for complete examples and behavior contracts.

For a function-by-function explanation, see the API Reference.

API

Components

AccessProvider

<AccessProvider user={user} superAdminRoles={["superadmin"]}>
  <App />
</AccessProvider>

Can

permission?: string | string[];
permissionGroup?: string | string[];
role?: string | string[];
deniedPermission?: string | string[];
deniedRole?: string | string[];
match?: "any" | "all";
condition?: "and" | "or";
resource?: AccessResource;
tenantId?: string | number;
tenantKey?: string;
ownerId?: string | number;
ownerKey?: string;
field?: string;
when?: AccessPolicy;
fallback?: ReactNode;
children: ReactNode | ((allowed: boolean) => ReactNode);

DisableWhenNoAccess

permission?: string | string[];
permissionGroup?: string | string[];
role?: string | string[];
deniedPermission?: string | string[];
deniedRole?: string | string[];
match?: "any" | "all";
condition?: "and" | "or";
resource?: AccessResource;
field?: string;
fallback?: ReactNode;
children: ReactElement | ((allowed: boolean) => ReactElement);

RouteGuard

permission?: string | string[];
permissionGroup?: string | string[];
role?: string | string[];
deniedPermission?: string | string[];
deniedRole?: string | string[];
match?: "any" | "all";
condition?: "and" | "or";
resource?: AccessResource;
field?: string;
redirectTo?: string;
navigate?: (to: string) => void;
fallback?: ReactNode;
children: ReactNode;

Utilities

hasPermission(user, permission, options?)
hasRole(user, role, options?)
isSuperAdmin(user, superAdminRoles?)
canAccess(user, accessRule, options?)
getAccessDecision(user, accessRule, options?)
filterMenuByAccess(menu, user, superAdminRoles?)
filterRoutesByAccess(routes, user, options?)
guardAction(options)
bsgGuardAction(options)
createAccessController(user, options?)

Types

AccessUser
AccessRule
AccessDecision
AccessDeniedEvent
AccessPolicy
AccessResource
AccessCondition
MatchType
MenuItem
PermissionGroups
RouteItem
CanProps
RouteGuardProps
AccessProviderProps
GuardActionOptions
BsgAccessCallback
BsgDeniedCallback
WithAccessOptions

AccessUser is flexible and accepts custom fields:

type AccessUser = {
  id?: string | number;
  name?: string;
  roles?: string[];
  permissions?: string[];
  isSuperAdmin?: boolean;
  [key: string]: unknown;
};

Development

npm install
npm run typecheck
npm test
npm run build
npm run dev

npm run dev starts Storybook for isolated component development.

To run the live Vite demo:

cd examples/demo-app
npm install
npm run dev

The demo imports permissionforge-react from the local package through file:../.., so it shows exactly how an app connects to this SDK during development.

Additional checks:

npm run api:check
npm run size
npm run verify

Additional Docs

Hindi Summary

permissionforge-react ek lightweight React + TypeScript access-control library hai. Isse aap admin panel, CRM, ERP, dashboard, ya BSG Backoffice me role aur permission ke basis par buttons, routes, menu items, fields, aur actions ko show, hide, disable, ya guard kar sakte hain. Backend authorization zaroor enforce karein; frontend guard sirf UX safety ke liye hota hai.

Publish

npm version patch
npm run build
npm publish --access public

For npm provenance:

npm publish --access public --provenance

The package publishes dist, README, license, changelog, security, contributing, FAQ, migration, docs, features, donation, copyright, and examples files.

Support

Developer: Pradeep Kumar Sheoran (Developer)
Company: BSG Technologies
Contact / WhatsApp: +91-8595147850
Website: https://bsgtechnologies.com

For support and a cup of coffee, visit BSG Technologies.

Donation / coffee support: UPI on mobile number +91-8595147850

This SDK is maintained as custom-owned source code for Permission Forge. It has no runtime dependency beyond React peer integration, and every permission feature is implemented inside this package.

Keywords

#React #TypeScript #RBAC #Permissions #AdminPanel #CRM #ERP #Dashboard #BSGBackoffice #BSGTechnologies