# Permissions screen

> A permission priming screen: each permission is a list row with a switch that asks the system only when turned on, a reminder preview drops in on a spring when notifications are allowed, a denial shows a banner with a way to Settings, and Not now is one tap away.

- Type: Component (screens)
- Access: Pro
- Page: https://goodui.dev/native/components/permissions-screen
- Markdown: https://goodui.dev/native/components/permissions-screen.md
- Registry item: https://goodui.dev/native/r/permissions-screen.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native permission priming, notification permission screen expo, location permission screen, pre-permission dialog, onboarding permissions, allow notifications screen

## When to use

- Right after sign-up, before the system dialogs, to explain why you ask for notifications, location, camera or contacts.
- When a denied permission needs a clear way back through Settings.

## When not to use

- Asking at the moment a feature needs it (attach a photo, share location once): ask in place with alert-dialog or directly.
- Settings the app controls by itself: use list-row switches in a settings screen.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add @goodui-native-pro/permissions-screen
npx expo install --fix
```

Pro: add `"@goodui-native-pro": { "url": "https://goodui.dev/native/r/pro/{name}.json", "headers": { "Authorization": "Bearer ${GOODUI_LICENSE_KEY}" } }` to the `registries` of components.json and put your license key in .env.local.

The component lands in `components/goodui-native/permissions-screen/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs banner, button, list-row, locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
// app/(onboarding)/permissions.tsx
import { Linking } from "react-native";
import { router } from "expo-router";
import * as Notifications from "expo-notifications";
import * as Location from "expo-location";
import { PermissionsScreen } from "@/components/goodui-native/permissions-screen/permissions-screen";

export default function PermissionsRoute() {
  return (
    <PermissionsScreen
      appName="Stride"
      onRequest={async (key) => {
        const result =
          key === "notifications"
            ? await Notifications.requestPermissionsAsync()
            : await Location.requestForegroundPermissionsAsync();
        return result.granted;
      }}
      onOpenSettings={() => Linking.openSettings()}
      onContinue={() => router.replace("/paywall")}
      onSkip={() => router.replace("/paywall")}
    />
  );
}
```

## Examples

### Already granted on open

```tsx
<PermissionsScreen defaultStatus={{ notifications: "granted" }} onRequest={request} onContinue={next} onSkip={next} />
```

### Your own permissions

```tsx
<PermissionsScreen
  title="Share your moments"
  preview={null}
  items={[
    { key: "camera", title: "Camera", description: "Take photos for your posts." },
    { key: "contacts", title: "Find friends", description: "We match numbers on your phone. Nothing is stored." },
  ]}
  onRequest={request}
  onContinue={next}
  onSkip={next}
/>
```

## API reference

### PermissionsScreen

`import { PermissionsScreen } from "@/components/goodui-native/permissions-screen/permissions-screen";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `appName` | `string` | – | Product name for the default copy and the preview notification. Without it the copy says "the app". |
| `title` | `string` | – | Headline. Same as `labels.title`. |
| `description` | `string` | – | One or two sentences under the headline. Same as `labels.description`. |
| `items` | `PermissionItem[]` | – | The permissions to prime, in order. Default notifications and location. |
| `defaultStatus` | `Record<string, PermissionStatus>` | `{}` | Statuses you already know (from the system) when the screen opens. Granted rows start switched on. |
| `onRequest` | `(key: string) => unknown` | – | Called when a switch is turned on. Ask the system here (expo-notifications, expo-location) and return a promise of the result: `"granted"` or `true` keeps the switch on; anything else flips it back with a shake and shows the item's denied banner. While pending the switch shows a spinner. Without it the switch simply toggles. |
| `onOpenSettings` | `() => unknown` | – | "Open settings" action on the denied banner, for example `Linking.openSettings()`. Hidden when omitted. |
| `onContinue` | `(enabled: Record<string, boolean>) => unknown` | – | Continue. Receives which items ended up on. Return a promise to show pending and a check on the button. |
| `onSkip` | `() => void` | – | "Not now". Leave the screen without asking anything. |
| `preview` | `ReactNode \| null` | – | Art above the headline. Defaults to a reminder preview that drops in when notifications turn on. `null` hides it. |
| `previewCopy` | `PermissionsPreviewCopy` | – | Words in the default preview, so it can speak for your app without replacing it. Neutral by default. |
| `labels` | `Partial<PermissionsScreenLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The headline is a header. Each row's switch carries the row title as its label (from list-row).
- A denied request flips the switch back and the warning banner is announced with its title and description.
- The reminder preview is decoration and hidden from screen readers.
- Continue and Not now are buttons in the bottom bar, within thumb reach.

## Motion

- Allowing notifications drops the reminder preview into place on the bouncy spring (from 14 pt above, 95 % scale, 40 % opacity) and the location chip rises in when location is on.
- While the system dialog is open the switch shows a spinner in its thumb; a denial flips it back with a shake and the banner slides down above the list.
- Reduced motion: the preview and chip only fade; switches and the banner follow their own reduced-motion branches.

## Haptics

- toggle-on or toggle-off when a switch changes, error when a request is denied (from switch). The banner stays silent so a denial buzzes once.
- tap and success from the Continue button.

## Performance

- Two shared values drive the preview on the UI thread; switches re-render only their row.

## Notes for AI

- onRequest(key) must ask the system and return a promise of true or 'granted' to keep the switch on; return anything else for a denial. Do not show your own spinner or alert.
- Turning a switch off never calls onRequest; it only records that the person does not want it. onContinue receives { key: boolean } for every item.
- Pass defaultStatus from the system's current permissions so granted rows start on and are not asked again.
- The default preview reacts to the keys 'notifications' and 'location'; pass preview={null} or your own node for other items.
- Default copy (items, headline, preview) is neutral so it fits any app. Pass items and title for your app, and previewCopy={{ title, body, location }} to make the default preview speak for it (for example a run reminder in a fitness app).
- Render it as the whole route with the header hidden; it handles safe areas itself.

## Related

- [List row](https://goodui.dev/native/components/list-row.md): Settings and navigation cells with a token-colored icon tile, title, subtitle and a trailing value, switch, badge, check or chevron, grouped in inset sections with headers and footers. On touch down the highlight floods in, the tile sinks and the chevron leans forward; on release the highlight lingers and fades.
- [Switch](https://goodui.dev/native/components/switch.md): An iOS-style switch with squash and stretch: the thumb stretches toward where it is going on touch down, then travels on a spring while the track color follows. Settings-row layout, tones, sizes and optimistic async changes.
- [Banner](https://goodui.dev/native/components/banner.md): An inline status banner that slides down from under the edge above and pushes the content down, flings away sideways with the finger, and can collapse on its own behind a draining hairline; includes an offline preset.
- [Welcome screen](https://goodui.dev/native/components/welcome-screen.md): A complete first-run welcome: an onboarding pager with parallax pages and a worm page dot, where each page's art plays as it settles (a weekly ring fills, a distance chart draws, a group of runners fans out), Get started and a Sign in link.

## Guidance for AI tools

Permissions screen: A permission priming screen: each permission is a list row with a switch that asks the system only when turned on, a reminder preview drops in on a spring when notifications are allowed, a denial shows a banner with a way to Settings, and Not now is one tap away. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
