Provider-agnostic React UI authorization gates.
@rccpr/auth-gate is a client-side rendering utility. It decides what to render in the UI based on auth state and permission decisions.
This library does not enforce backend authorization. Keep server-side authorization checks in place for all protected operations.
bun add @rccpr/auth-gateThe API is factory-first and Show-first:
createAuthGate(adapter)Show(canonical)Protect(alias ofShow)SignedInSignedOutuseAuthGate
import{createAuthGate}from"@rccpr/auth-gate";typeUser={id: string;email: string};constgate=createAuthGate<User,string>({mode: "sync",useAuthState: ()=>({user: {id: "u_1",email: "dev@example.com"},isAuthenticated: true,isLoading: false,}),useAuthorizationDecision: (check)=>{if(check&&"permission"incheck&&check.permission==="org:admin"){return{status: "allowed"};}return{status: "denied"};},});exportconst{
AuthGateProvider,
Show,
Protect,
SignedIn,
SignedOut,
useAuthGate,}=gate;sync: decisions come from hook-native adapter methods (use*)async: decisions come from promise-native adapter methods (get*) and a resolver hook
The normalized async load state is:
alloweddeniedpendingerror
Show authz checks follow Clerk-style has() input objects:
{ permission: "org:billing:manage" }{ role: "org:admin" }{ feature: "teams" }{ plan: "pro" }
<Showwhen="signed-in"fallback={<SignInPrompt/>}><Dashboard/></Show><Showwhen="signed-out"fallback={<Dashboard/>}><MarketingPage/></Show><Showwhen={{permission: "org:admin"}}fallback={<NoAccess/>}loadingFallback={<LoadingAccess/>}><AdminPanel/></Show><Protectwhen={{permission: "org:billing"}}><BillingSettings/></Protect>import{createAuthGate}from"@rccpr/auth-gate";constgate=createAuthGate({mode: "async",useAuthState: useMyAuthState,getAuthorizationDecision: async(check)=>{constallowed=awaitapi.can(check);returnallowed ? {status: "allowed"} : {status: "denied"};},asyncResolver: {useDecision: useAuthorizationDecisionFromQuery,},});useAuthGate returns normalized auth data and an evaluate(check) helper:
constsnapshot=useAuthGate();conststate=snapshot.evaluate({permission: "org:read"});For async adapters, evaluate(check) returns pending and Show should be preferred for rendered gating.
bun install
bun run test
bun run typecheck
bun run lint
bun run build