React
<RbacProvider> + <Can I="..." a="..."> + usePermission() — import ... from 'rbac-fs/react'. Naming follows CASL's convention (I = action, a = resource) for a familiar mental model.
Usage
import { RBACClient } from 'rbac-fs/client';
import { Can, RbacProvider, usePermission } from 'rbac-fs/react';
function ApproveButton() {
return (
<Can I="approve" a="invoice" fallback={<span>You can't approve invoices</span>}>
<button>Approve invoice</button>
</Can>
);
}
function OwnExpenseReportButton({ ownerId }: { ownerId: string }) {
// context threads through to conditional (`when`/`condition`) grants
return (
<Can I="approve" a="expense-report" context={{ owner_id: ownerId }}>
<button>Approve my expense report</button>
</Can>
);
}
function ImperativeCheck() {
const can = usePermission(); // same client.can(), for use outside JSX
return can('invoice', 'approve') ? <p>Allowed</p> : <p>Denied</p>;
}
function App({ client }: { client: RBACClient }) {
return (
<RbacProvider client={client}>
<ApproveButton />
<OwnExpenseReportButton ownerId="u1" />
<ImperativeCheck />
</RbacProvider>
);
}RBACClient is built from a permission snapshot your backend resolves and returns — never read .rbac/ files directly in the browser. Full verification (rendered headlessly with react-test-renderer): examples/12-react-usage.tsx.
Next.js: no separate adapter needed —
rbac-fs/react works as-is on top of it. Fetch and hydrate the snapshot server-side; never read .rbac/ files during SSR render.