client overview
Client-side permission checks for React, Vue, and vanilla JavaScript. Server-driven hydration with sub-millisecond lookups.
How it works
duck-iam ships client libraries for browser permission checks. The recommended pattern is server-driven: generate a PermissionMap on the server, send it to the client, and use it for UI decisions.
Every client library supports the same core operations:
can(action, resource, resourceId?, scope?)— returnstruewhen the permission is grantedcannot(action, resource, resourceId?, scope?)— returnstruewhen the permission is denied
Client checks are synchronous lookups against the pre-computed map. No network requests during checks.
Pick a client
| Framework | Doc | Subpath |
|---|---|---|
| React | client/react | @gentleduck/iam/client/react |
| Vue | client/vue | @gentleduck/iam/client/vue |
| Vanilla JS | client/vanilla | @gentleduck/iam/client/vanilla |
Use the PermissionMap reference for the wire format, key encoding, and buildPermissionKey() helper.
Server-driven pattern
- Security — Permission logic runs on the server where policies and roles are stored. The client only sees the final boolean results.
- Performance — Client-side checks are instant object lookups. No async, no network, no engine evaluation.
- Consistency — The server is the single source of truth.
- Simplicity — The client libraries are thin wrappers around a flat object.
Refreshing permissions
When a user's role changes (promoted, joined an org, feature flag flips), fetch a new permission map from the server and update the client.
React:
// Re-render the server component (Next.js router.refresh())
// or use usePermissions with a refetch trigger// Re-render the server component (Next.js router.refresh())
// or use usePermissions with a refetch triggerVue:
const { update } = useAccess()
const newPerms = await fetch('/api/me/permissions').then((r) => r.json())
update(newPerms)const { update } = useAccess()
const newPerms = await fetch('/api/me/permissions').then((r) => r.json())
update(newPerms)Vanilla:
const newPerms = await fetch('/api/me/permissions').then((r) => r.json())
access.update(newPerms) // subscribers are notified automaticallyconst newPerms = await fetch('/api/me/permissions').then((r) => r.json())
access.update(newPerms) // subscribers are notified automatically