How should a React Native document-scanning app time and sequence its camera and photo-library permission requests so users rarely deny them for good?
answer
- ask at the moment of use
- your own explainer before the system prompt
- one permission per action
- the picker avoids a library permission
- rationale dialog: any button continues
basics
~20 sAsk for each permission only when the user starts the feature that needs it, explain first in your own UI, request one permission per action, and avoid asking at all where the system photo picker can do the job.
solid answer
~50 sThe system prompt is a scarce resource: iOS shows it once, and Android usually stops after a second refusal. So a scanner asks for the **camera** only when the user taps **Scan**, not at launch, and first shows its own short explainer with a Continue button, because a refusal of your own screen costs nothing while a refusal of the system prompt may be permanent. It asks for **photo-library** access only if the user wants to import, and where the platform's system photo picker is available, it asks for nothing, since the picker hands over only the chosen images. On Android, `PermissionsAndroid.request`'s rationale is shown only after a first refusal, and whichever button the user taps, the system prompt follows, so it cannot replace your own pre-prompt. Use `requestMultiple` only when one action genuinely needs several permissions.
go deeper
Recall the rule of thumb: ask for a permission when the user starts the feature that needs it, and explain why first.
Explain why system prompts are scarce on both platforms, when the PermissionsAndroid rationale appears, and when requestMultiple fits.
Design the flow end to end: pre-prompt, one permission per action, picker-first import, blocked-state recovery, and refusal-rate metrics per release.
Set product-wide rules on which features may ask, how asks are reviewed, and which permission-free alternatives teams must try first.
## Why timing matters more than wording Both platforms ration the system permission prompt: - **iOS** shows each permission's prompt once; after a refusal, only the Settings app can change it. - **Android** shows it again after a first refusal, but a second refusal usually makes it permanent, which React Native reports as `'never_ask_again'`. A refused system prompt is therefore expensive, while a refused screen of your own costs nothing: you can show it again next time. The whole design follows from that asymmetry. ## Ask at the moment of use A document scanner has two sensitive moments: 1. **Scan** — the user taps the button that opens the camera. That is the moment the camera permission makes sense to them. 2. **Import** — the user wants to turn an existing photo into a scan. Only then does photo-library access matter. Asking for both at launch, before the user has seen the app do anything, collects the most refusals. Asking at the moment of use means the user already knows why. ## Pre-prompt with your own screen Before calling the system, show a short in-app explainer: one sentence on what the camera is for, a **Continue** button and a **Not now** button. - **Continue** leads to the system request, with a user who has just agreed in principle. - **Not now** leaves the prompt unused, so you can ask again later without burning it. ## What React Native's rationale does and does not do `PermissionsAndroid.request(permission, rationale)` accepts a `title`, `message` and optional button labels, but: | Property | Behaviour | |---|---| | When it appears | only when Android reports a rationale should be shown, normally after one refusal | | First request | not shown; the system prompt appears directly | | Button handling | whichever button is tapped, or if the dialog is dismissed, the system prompt follows | | Platform | Android only | So it is a "second chance" explainer, not a pre-prompt, and a **Cancel** label on it is misleading. A pre-prompt with a real "not now" path has to be your own UI. ## Prefer no permission at all The strongest design removes a prompt entirely: - The **system photo picker** on current iOS and Android versions returns only the images the user selects, without photo-library permission. Import can often work with no prompt. - **Saving** a scan to Photos on iOS needs only the add-only key, `NSPhotoLibraryAddUsageDescription`, which exposes nothing already in the library. - On Android 14 and later, a user can grant partial media access; `PermissionsAndroid.PERMISSIONS.READ_MEDIA_VISUAL_USER_SELECTED` names that state. ## One permission per action `requestMultiple` runs one flow for several permissions and resolves a map of results. It suits an action that truly needs all of them at once, such as video with sound. For a scanner, camera and library belong to different actions, so request them separately, each at its own moment. ## Asking again after a first refusal On Android, a first refusal of the system prompt leaves the result at `'denied'`, not `'never_ask_again'`. The prompt is still available, but it is the last one: a second refusal usually makes it permanent. - Do not re-ask immediately after the refusal; wait for the next time the user starts the feature. - Show your own explainer again first. On this second attempt, `PermissionsAndroid.request` will also show the rationale you pass, because Android now reports a rationale as appropriate, so keep the two messages consistent rather than stacking two different explanations. - Cap the attempts: if the user declines your own explainer twice, stop offering the camera and lead with the import path. ## A flow that holds up 1. `check` the camera permission when the scan screen mounts; if granted, show the preview. 2. If not, show the pre-prompt on the first **Scan** tap. 3. On **Continue**, call `request`; map the result to `granted`, `canAsk` or `blocked`. 4. On `blocked`, explain and offer `Linking.openSettings()`, plus the import path. 5. Measure the share of users in each state per release. ## Signals worth reviewing - A high refusal rate on the first prompt means the ask is too early or unexplained. - Rising `'never_ask_again'` rates after a release usually trace to a new, earlier ask.
- Why is putting a Cancel button in PermissionsAndroid's rationale object misleading?React Native shows the rationale through an Android alert and, whichever button is pressed or if the dialog is dismissed, goes on to show the system prompt. A Cancel label suggests the user can back out, but they get the system dialog anyway. A real opt-out has to come from your own pre-prompt screen.
- Should a React Native scanner request the camera and photo-library permissions together at onboarding to reduce interruptions?No. Bundled requests at onboarding lack context, so users refuse more, and every refusal burns a scarce prompt. Import may not even need a permission when the system photo picker is available. Ask for each at the action that uses it; the extra interruption is one tap the user expects.
Asking for camera access at launch is like a waiter asking for a tip before taking the order; asking when the user taps Scan is asking after they have seen the service, when the reason is obvious.
saying these in an interview costs you the question
- Ask for every permission at launch so the user is not interrupted later.
- The PermissionsAndroid rationale is shown before the very first prompt.
- Pressing Cancel on the rationale dialog stops the system prompt.
- Importing a photo always needs full photo-library permission.
- requestMultiple is the best default for any screen using two permissions.