DEV Community

Cover image for ❌ Stop Writing if (user.role === "admin") Everywhere — Clean RBAC in React
Parsa Jiravand
Parsa Jiravand

Posted on

❌ Stop Writing if (user.role === "admin") Everywhere — Clean RBAC in React

Most React apps start clean… and then slowly turn into this:

if (user?.role === "admin") { ... }
Enter fullscreen mode Exit fullscreen mode

Scattered across buttons, pages, and components.

At first, it works.
Then it spreads.
Then it breaks.

  • ❌ Duplicate logic everywhere
  • ❌ Hard to refactor
  • ❌ Inconsistent UX
  • ❌ Easy security mistakes

There’s a better way: centralized, declarative RBAC.

That’s exactly what advanced-react-role-guard solves.


👉 Try it live (interactive demo):
https://advanced-react-role-guard-website.netlify.app/

👉 Docs:
https://advanced-react-role-guard-doc.netlify.app/

👉 GitHub:
https://github.com/parsajiravand/react-role-guard


🧠 The Problem in One Line

You’re mixing authorization logic with UI rendering.

Instead, you want:

User → Roles → Permissions → Access decision → What you render


❌ Before (what most apps look like)

{user?.role === "admin" && <AdminPanel />}

{user?.permissions?.includes("post:create") && (
  <button>Create Post</button>
)}
Enter fullscreen mode Exit fullscreen mode

✅ After (clean + scalable)

<Can role="admin">
  <AdminPanel />
</Can>

<Can permissions={["post:create"]}>
  <button>Create Post</button>
</Can>
Enter fullscreen mode Exit fullscreen mode

Same logic. 10x cleaner.


🚀 Why developers love this

  • No more scattered role checks
  • One source of truth for permissions
  • Cleaner components (focus on UI only)
  • Better UX with built-in fallbacks
  • Works with modern stacks (Next.js, Vite)

It feels like moving from manual checks → declarative UI.


📦 Install

npm install advanced-react-role-guard
Enter fullscreen mode Exit fullscreen mode

⚙️ How it works

Wrap your app with RoleGuardProvider:

import { RoleGuardProvider, Can, useCan } from "advanced-react-role-guard";

function App() {
  return (
    <RoleGuardProvider
      user={{
        roles: ["editor"],
        permissions: ["post:create"],
      }}
      config={{
        admin: ["*"],
        editor: ["post:create", "post:update"],
        viewer: ["post:read"],
      }}
      features={{
        newDashboard: true,
        aiSearch: false,
      }}
    >
      <Dashboard />
    </RoleGuardProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now every component can check access cleanly.


🎯 Pattern 1: Declarative UI (<Can>)

<Can role="admin" fallback={<p>Admin only</p>}>
  <AdminPanel />
</Can>

<Can
  permissions={["order:edit", "order:cancel"]}
  match="any"
  fallback={<p>Read-only access</p>}
>
  <OrderActions />
</Can>
Enter fullscreen mode Exit fullscreen mode
  • match="any" → at least one permission
  • match="all" → must have all permissions

🎯 Pattern 2: Hooks (useCan)

function PostToolbar() {
  const canPublish = useCan({
    permissions: ["post:create", "post:publish"],
    match: "all",
  });

  return (
    <>
      <button disabled={!canPublish}>Publish</button>
      {!canPublish && <span>No permission</span>}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

🎯 Pattern 3: Route Protection

import { Guard } from "advanced-react-role-guard";
import { Navigate } from "react-router-dom";

<Route
  path="/admin"
  element={
    <Guard role="admin" fallback={<Navigate to="/403" replace />}>
      <AdminPage />
    </Guard>
  }
/>
Enter fullscreen mode Exit fullscreen mode

🎯 Pattern 4: Wildcards

const config = {
  admin: ["*"],
  support: ["user:*"],
  editor: ["post:*"],
};
Enter fullscreen mode Exit fullscreen mode
  • * → everything
  • user:* → all user permissions

🎯 Pattern 5: Feature Flags

<Feature name="newDashboard" fallback={<LegacyDashboard />}>
  <DashboardV2 />
</Feature>
Enter fullscreen mode Exit fullscreen mode

💼 Real-world example

Imagine a SaaS dashboard:

  • Admin → full access
  • Editor → create + edit
  • Viewer → read-only

Define once:

config={{
  admin: ["*"],
  editor: ["post:create", "post:update"],
  viewer: ["post:read"],
}}
Enter fullscreen mode Exit fullscreen mode

Now your entire app follows this rule automatically.


⚠️ Important

This library handles UI-level authorization only.

You still need backend validation for real security.


🔗 Links


🎯 Final Thoughts

If your app has:

  • roles
  • permissions
  • dashboards
  • multiple user types

You don’t need more if statements.

You need structure.

👉 Try the demo
👉 Install in 2 minutes
👉 Clean your codebase

If you use it, drop your stack (Next.js? Vite?) — I’d love to see how you structure your RBAC.

Top comments (0)