Real apps use the camera, location, and notifications — all gated by permission. Request access the right way with Expo modules and handle the case where the user says no.
Why: the OS guards sensitive features — camera, location, contacts, notifications — behind a permission the user must grant. You request it at the moment you need it (not all up front), and you must handle denial gracefully. Expo modules expose a consistent request/check API per feature.
app requests permission ─▶ OS shows the system prompt
│
granted ─▶ use the feature
denied ─▶ explain why & offer to open Settings (never crash)Why: expo-image-picker is the quickest way to let users pick a photo or take one — it requests the permission for you and returns the chosen asset. Always check the result: the user can cancel or deny.
// npx expo install expo-image-picker
import * as ImagePicker from 'expo-image-picker'
async function pickImage() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
})
if (!result.canceled) {
return result.assets[0].uri // use the picked image
}
}Why: expo-location requests permission and returns the device position. Request foreground permission, check it was granted, then read the location. Note: never assume permission — branch on the status the request returns.
// npx expo install expo-location
import * as Location from 'expo-location'
async function getLocation() {
const { status } = await Location.requestForegroundPermissionsAsync()
if (status !== 'granted') {
return null // user declined — show a fallback
}
const pos = await Location.getCurrentPositionAsync({})
return pos.coords // { latitude, longitude, ... }
}Note: a permission can be permanently denied — then re-requesting does nothing, and your only recourse is to send the user to the system Settings. Always design the denied path: explain why the feature needs access, and offer a button that opens Settings with Linking.openSettings(). Never let a denied permission break the screen.
import { Linking } from 'react-native'
function PermissionDenied() {
return (
<View style={{ padding: 16, gap: 8 }}>
<Text>Camera access is needed to scan receipts.</Text>
<Button title="Open Settings" onPress={() => Linking.openSettings()} />
</View>
)
}