# Permission-gated UI Authorization is enforced on the **server** (see [`authorization.md`](authorization.md)). The frontend never decides what a user is *allowed* to do — it asks the API and renders accordingly. The API exposes per-record permissions through the `permission(action:)` field, which a component selects as a boolean alias (`canUpdate`, `canDelete`, …) on the node it renders. This guide covers how to **consume** those booleans. It does not grant access; hiding a button is a UX nicety, not a security control — the mutation is still authorized server-side. ## Related guides | Topic | Guide | |-------|--------| | Server-side IAM policies and actions | [`contrib/claude/authorization.md`](authorization.md) | | Fragments, colocated data | [`contrib/claude/relay.md`](relay.md) | | Component shape and props | [`contrib/claude/react-components.md`](react-components.md) | ## Select permissions in the fragment that needs them A component that renders an action selects the matching permission **in its own fragment**, aliased to a `can…` boolean. Keep the action string identical to the IAM action it guards. ```tsx const documentListItemFragment = graphql` fragment DocumentListItem_document on Document { id title canUpdate: permission(action: "core:document:update") canDelete: permission(action: "core:document:delete") ...EditDocumentDialog_document } `; ``` Colocate the permission with the action it gates — never drill a `canDelete` boolean down as a prop from a parent (the same data-as-props rule as everywhere else; see [`react-components.md`](react-components.md#props-are-for-configuration-and-composition-not-data)). Spread a child's fragment (`...EditDocumentDialog_document`) when you forward the node to that child as a fragment key. `useFragment` returns plain data, but Relay keeps the spread fragment refs on it, so the resolved `document` doubles as the key the child's own `useFragment` expects. Without the spread, `documentKey={document}` would hand the child masked data with no ref and fail at runtime (see [`relay.md`](relay.md)). ## Gate the action on the boolean Read the boolean via `useFragment` and gate the control. Default to **hiding** an action the user cannot perform; **disable** (with an explanatory tooltip) only when the action's *absence* would be confusing. ```tsx export function DocumentListItem({ documentKey }: DocumentListItemProps) { const document = useFragment(documentListItemFragment, documentKey); return (