# Using roles in UI extensions
Learn how to include user roles in UI Extensions to tailor functionality to different roles.
Stripe Apps UI extensions can read the active user’s role in the Dashboard. Apps can expose different functionality to different user roles.
The UI Extension SDK provides valuable information about the end user of your app. The `roles` field of the `userContext` object gives a list of the active user’s roles. You can tailor the app’s content based on the user’s role, using the roles in the user context.
In addition to roles, use `appContext.authorizedPermissions` to check which permissions the app has for the current context. For a full list of fields, see `appContext` in the [UI Extension SDK API](https://docs.stripe.com/stripe-apps/reference/extensions-sdk-api.md).
## How to determine the user’s Dashboard role
Extensions have a `userContext` prop that’s populated with information about the active Dashboard user. This object has a `roles` field, which is an array of `RoleDefinition` objects for each role that the active user is attributed to.
A role definition has these fields:
| Field name | Type | Example | **Description** |
| --- | --- | --- | --- |
| type | ‘builtIn’ | ‘custom’ | builtIn | Specifies the role type. Custom roles are only available to [private apps](https://docs.stripe.com/stripe-apps/distribution-options.md#share-with-team-members). |
| id | string | developer | A stable, machine-readable identifier for the user role. Unlike `name`, this value won’t change when role display names are updated and is safe to use for comparisons. |
| name | string | Developer | Plain language name for the user role. Use `id` for programmatic comparisons because the `name` can change. |
The `id` field provides a stable identifier for the user role that you can use to modify your UI extension’s functionality. Use `id` instead of `name` for programmatic role comparisons to prevent breakages if the role display names change.
## Built-in roles
Stripe provides a set of built-in roles available to all accounts. These roles are available in both public and private apps. For the full list of built-in roles and what each one can do, see [User roles](https://docs.stripe.com/get-started/account/teams/roles.md).
## Custom user roles (private apps only)
Custom roles are account-specific roles created by account administrators in the Stripe Dashboard under **Settings** > **Team and security**. Because custom roles are specific to a given account, they’re only available for [private apps](https://docs.stripe.com/stripe-apps/distribution-options.md#share-with-team-members). As an app developer, you can’t create or define custom roles. To use custom roles in your app, coordinate with the account administrator on which custom roles and IDs to create.
When a custom role is exposed to your app, its `id` is the role’s token with the `role_` prefix stripped. For example, a custom role with the token `role_abc123` appears in `userContext.roles` as `{ id: 'abc123', type: 'custom', name: 'Shipping Manager' }`.
Custom roles are never exposed to public apps. If your app is distributed publicly, `userContext.roles` will only ever contain built-in roles.
## Tailoring content based on the Dashboard role
A common use of this information is to conditionally display content based on the user role. Below is an example app that shows content tailored to particular user roles.
```jsx
import { Badge, Box, Inline, ContextView } from "@stripe/ui-extension-sdk/ui";
import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";
const App = ({ userContext }: ExtensionContextValue) => {
// Use `id` for stable role comparisons instead of `name`
const isAdmin = userContext?.roles?.some(role => role.id === 'admin');
const isDeveloper = !isAdmin && userContext?.roles?.some(role => role.id === 'developer');
const isAnotherRole = !isDeveloper && !isAdmin;
return (
Active user roles: {userContext?.roles?.map(role => {role.name})}
{ isAdmin && (Only admin users can see this message.) }
{ isDeveloper && (Only developers users can see this message.) }
{ isAnotherRole && (Only users who are not admins or developers can see this message.) }
);
};
export default App;
```

The result of the example app when viewing the app as an Administrator user
## Restrict access based on the Dashboard role
Use role checks to restrict entire views to specific roles. This is useful for full-page apps or any view that’s only accessible to certain team members.
Role-based checks only apply to the UI. They control what users can see but they don’t control what users can do through the API. The app’s sandbox loads regardless of the user’s role. If your app makes backend API calls, also enforce role restrictions server-side.
The following example renders a blocked state for users who don’t have the required role:
```jsx
import { ContextView, Box } from "@stripe/ui-extension-sdk/ui";
import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";
const App = ({ userContext }: ExtensionContextValue) => {
const hasAccess = userContext?.roles?.some(role => role.id === 'admin');
if (!hasAccess) {
return (
You need the Administrator role to access this feature.
);
}
return (
{/* content for admins only */}
);
};
export default App;
```
For apps with multiple views that each need role protection, create a shared wrapper component to avoid repeating the check:
```jsx
import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";
import { ContextView, Box } from "@stripe/ui-extension-sdk/ui";
type RoleGateProps = {
userContext: ExtensionContextValue["userContext"];
allowedRoles: string[];
children: React.ReactNode;
};
const RoleGate = ({ userContext, allowedRoles, children }: RoleGateProps) => {
const hasAccess = userContext?.roles?.some(role =>
role.id !== undefined && allowedRoles.includes(role.id)
);
if (!hasAccess) {
return (
You don't have permission to use this feature.
);
}
return <>{children}>;
};
```
Keep the following in mind when implementing role-based access:
- `userContext.roles` can be `undefined` in some contexts, such as embedded apps. Always use optional chaining when reading it.
- There is no manifest-level support for role restrictions. There is no `allowed_roles` field in `stripe-app.json`. Every view that needs protection must implement the check in its component code.
- For public apps, only built-in role IDs are available. Custom roles are silently filtered out and never appear in `userContext.roles` for public apps.
## See also
- [Build a UI](https://docs.stripe.com/stripe-apps/build-ui.md)
- [UI Extension SDK API reference](https://docs.stripe.com/stripe-apps/reference/extensions-sdk-api.md)
- [User roles](https://docs.stripe.com/get-started/account/teams/roles.md)
- [Distribution options](https://docs.stripe.com/stripe-apps/distribution-options.md)