# RequireRole

Render-gate that requires both an active session and a named role string in the user's `app_metadata.roles` claim. Compose at the top of a protected subtree so the gate runs once. Roles are issued backend-side by writing `auth.users.app_metadata.roles`; this component is read-only.

[View examples](https://marsam.rahhal.app/components/require-role) · [Agent index](https://marsam.rahhal.app/llms.txt)

**Import:** `@rahhal/auth/react`

**Category:** `auth`

## States (mapped to `useSession().status` + `useRole(role).hasRole`)

| status / role | renders |
| --- | --- |
| `booting` | `parsing-hash` | `pending` (default: nothing) |
| `signed-in` && hasRole | `children` |
| `signed-in` && !hasRole | `deniedFallback` (default: terse inline alert) |
| `signed-out` | `fallback` (default: "open from Rahhal app") |
| `error` | `errorFallback` |

## Props

| prop | type | purpose |
| --- | --- | --- |
| role | string | required role string (e.g. `"gmaplist_admin"`) |
| children | ReactNode | rendered when signed-in and role present |
| pending | ReactNode | shown during boot / fragment-parsing |
| fallback | ReactNode | shown when signed out — pass your <LoginScreen> |
| deniedFallback | ReactNode | shown when signed in without the role — pass <AccessDeniedScreen> |
| errorFallback | ReactNode | shown on unrecoverable session errors |

## Companion hook

`useRole(role)` returns `{ hasRole, roles, status }`. Use for multi-tier UIs that render differently for `super_admin` vs `viewer` without reaching into `useJwtClaims()` directly.

## Where roles come from

Write `auth.users.app_metadata.roles` (a `string[]`) via the Supabase dashboard or a service-role admin script. The JWT mirrors that array into its `app_metadata` claim, which is what `<RequireRole>` reads.
