Learn React Native - Media, Camera & Permissions
Episode 12 of 23

Learn React Native - Media, Camera & Permissions

This episode covers media: the camera with react-native-vision-camera, image picker, image resize and compression, permission management with react-native-permissions, usage descriptions in Info.plist, and UX when the user denies permission.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

The camera and gallery are the features that most often trigger permission questions and the first crash in a mobile app. One wrong declaration in Info.plist or AndroidManifest.xml, and the app closes immediately when the user taps the camera button.

Episode 12 covers media end to end: accessing the camera with react-native-vision-camera, choosing images with an image picker, resizing and compressing, then managing permissions with react-native-permissions — including usage descriptions in Info.plist and the user experience when permission is denied.

Accessing the Camera with VisionCamera

Installing and Setting Up

VisionCamera is a modern JSI-based camera library. In an Expo project, install it via npx expo install so the version matches the SDK:

Install VisionCamera
npx expo install react-native-vision-camera

Add the camera usage description in Info.plist for iOS and the permission declaration in AndroidManifest.xml for Android before running the app.

Building a Camera Screen

JSCamera with VisionCamera
import { Camera, useCameraDevice } from "react-native-vision-camera";
import { Text, View } from "react-native";
 
export function KameraScreen() {
  const device = useCameraDevice("back");
 
  if (device == null) {
    return <Text>Kamera tidak tersedia</Text>;
  }
 
  return (
    <View style={{ flex: 1 }}>
      <Camera
        style={{ flex: 1 }}
        device={device}
        isActive={true}
        photo={true}
      />
    </View>
  );
}

useCameraDevice("back") selects the rear camera. The isActive property controls when the camera is on — turn it off when the screen isn't visible to save battery. photo={true} enables photo capture mode.

Taking a Photo

The takePhoto() result path from the camera ref — a temporary file — is ready for further processing: resize, compress, or upload to the server.

Image Picker and Processing

Not every flow needs the camera directly. To pick images already in the gallery, use an image picker:

Install image picker
npm install react-native-image-picker
JSChoosing an image from the gallery
import { launchImageLibrary } from "react-native-image-picker";
 
function pilihGambar() {
  launchImageLibrary({ mediaType: "photo" }, (res) => {
    if (res.didCancel) return;
    const uri = res.assets?.[0]?.uri;
    prosesGambar(uri);
  });
}

Note res.didCancel — the user closing the picker without selecting is a normal occurrence, not an error. res.assets?.[0]?.uri uses optional chaining because the array can be empty.

Resize and Compression

Original images from modern cameras are a dozen megabytes. Uploading them raw burns data quota and slows down the UI. Resize and compress before uploading:

Install image resizer
npm install react-native-image-resizer
JSResizing an image before upload
import ImageResizer from "react-native-image-resizer";
 
async function siapkanUpload(uri) {
  const hasil = await ImageResizer.createResizedImage(uri, 1200, 1200, "JPEG", 85);
  return hasil.uri;
}

createResizedImage(uri, 1200, 1200, "JPEG", 85) brings the resolution down to a maximum of 1200 pixels and JPEG quality of 85 percent — enough for screen display, light on the network.

Permission Management

react-native-permissions

Manage permissions across platforms with react-native-permissions — one API for iOS and Android:

Install react-native-permissions
npm install react-native-permissions
JSChecking and requesting camera permission
import { check, request, PERMISSIONS, RESULTS } from "react-native-permissions";
import { Platform } from "react-native";
 
const kamera =
  Platform.OS === "ios" ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA;
 
async function pastikanIzin() {
  const status = await check(kamera);
  if (status === RESULTS.DENIED) {
    return request(kamera);
  }
  return status;
}

Platform.OS === "ios" selects the correct permission constant per platform. The right sequence: check first, request only if the status is DENIED, and don't request again once it's BLOCKED — requesting again on iOS when already blocked silently returns without a dialog.

Usage Descriptions in Info.plist

iOS requires a usage description for every permission used. Without it, the app crashes when the permission is requested:

Usage description in Info.plist
NSCameraUsageDescription: Aplikasi memakai kamera untuk memotret foto profil.
NSPhotoLibraryUsageDescription: Aplikasi memakai galeri untuk memilih gambar.

This description text appears in the iOS permission dialog — write something clear and honest about the purpose of the feature.

UX When Permission Is Denied

Guiding the User to Settings

Users have the right to deny permission. When the status is BLOCKED, the dialog can no longer be shown — the only path is system settings. Handle it by showing an explanation and a button to open settings:

JSOpen settings when blocked
import { openSettings } from "react-native-permissions";
 
async function saatDiblokir() {
  const izin = await pastikanIzin();
  if (izin === RESULTS.BLOCKED) {
    openSettings();
  }
}

openSettings() opens the app's settings screen in the system. The combination of a status check, an explanatory message, and a path to settings helps the user understand why the feature needs permission and how to enable it again.

Design That Still Works

The camera feature isn't everything. When permission is denied, show alternatives: manual input, upload from the gallery, or other features that don't need permission. A good app stays useful even when some permissions are denied.

Warning

Don't request many permissions at once at startup. The system and users will be suspicious, and denial rates rise dramatically. Request permission in the context of the relevant feature and explain its benefit before the dialog appears.

Closing

Episode 12 mastered media: the camera with VisionCamera, image selection and processing, cross-platform permissions with react-native-permissions, correct usage descriptions, and the user experience when permission is denied.

Key takeaways:

  • VisionCamera uses JSI and needs a permission declaration on each platform.
  • Turn off the camera's isActive when the screen isn't visible to save battery.
  • Resize and compression are mandatory before uploading images.
  • Check first, request when DENIED, don't request again when BLOCKED.
  • iOS crashes without a usage description in Info.plist.
  • When permission is denied, provide alternatives and a path to settings.

In the next episode, episode 13, we'll discuss secure storage and credentials: Keychain and Keystore for tokens, avoiding secrets in the bundle, per-build .env configuration, and habits that keep secrets from ever being committed.

Learn React Native - Media, Camera & Permissions | Learn React Native