Device APIs: Camera, Location, and Permissions
One of Expo's biggest advantages is instant access to native device features through simple JavaScript APIs, no native code required. Nearly all of them follow the same permission pattern.
The permission pattern
import * as Location from 'expo-location';
async function getCurrentLocation() {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
console.log('Permission denied');
return null;
}
const location = await Location.getCurrentPositionAsync({});
return location.coords;
}
Every device API works the same way: request permission (the OS shows a native prompt the first time), check the result, then use the API only if granted. Never assume permission, always request and check.
Using the camera
import { CameraView, useCameraPermissions } from 'expo-camera';
function ScannerScreen() {
const [permission, requestPermission] = useCameraPermissions();
if (!permission) return null; // still loading
if (!permission.granted) {
return <Button title="Grant camera access" onPress={requestPermission} />;
}
return <CameraView style={{ flex: 1 }} />;
}
useCameraPermissions() is a hook version of the same request/check pattern, common for APIs used directly inside a component.
Declaring permissions in app.json
Some permissions (especially on iOS) also need a usage description declared in app.json, explaining why your app wants access, shown to the user in the native permission dialog:
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "This app uses the camera to scan QR codes."
}
}
}
}
NOTE
Most of Expo's device modules (expo-camera, expo-location, expo-notifications, expo-contacts, ...) are separate packages you install individually with npx expo install expo-camera, using expo install instead of plain npm install ensures you get a version compatible with your Expo SDK version.