How to Manage Permissions in React Native?

Alexey Karimov

Have you ever built a React Native app that needed access to the camera, microphone, or location? However, they don’t work because the required permissions weren’t set up correctly. If you have experienced the same, you need to learn how to manage permissions in React Native.

Permissions can often be one of the most confusing yet essential parts of mobile development, especially when dealing with differences between Android, iOS, and web environments. In this guide, we’ll show how to set up and manage permissions in both React Native CLI and Expo projects, use react-native-permissions for runtime handling, and follow best practices to keep your app ready.

Understanding Permissions in React Native

Every app that accesses device features must request permission from the user. In React Native, permissions work at two levels: build-time and runtime.

At build time, permissions are declared in native files, so the operating system knows which features your app intends to use. Android uses AndroidManifest.xml, iOS uses Info.plist, and Expo projects use app.json.

At runtime, your app must ask for these permissions before accessing those features. For example, even if the camera permission is declared in Android’s manifest, the user must still grant it when the app runs.

You can manage permissions using React Native’s built-in APIs like PermissionsAndroid or the react-native-permissions library, which provides a unified way to check and request permissions across Android, iOS, and Windows.

Setting Up Permissions in React Native CLI

When you build a React Native app using the CLI, you are working directly with the native Android and iOS layers. That means you need to configure permissions both in your native files and through JavaScript at runtime. Let’s break that down clearly.

1. Installing the permissions library

While React Native provides a basic PermissionsAndroid API for Android, most developers prefer using the react-native-permissions library because it works across both Android and iOS. To install it, open your project directory and run:

npm install react-native-permissions
cd ios && pod install

Note: You must re-run pod install every time you change the permission list in your Podfile.

This library helps you request, check, and manage permissions uniformly across platforms.

2. Configuring for iOS

For iOS, permissions must be declared in a special file called Info.plist. This file tells the system what sensitive data or features your app might use. For example, if your app uses the camera, you must add a line that explains why:

<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take profile pictures.</string>

Without this explanation, your app will crash when trying to access the camera. Apple also reviews these messages during App Store submission, so make them clear and specific.

3. Configuring for Android

On Android, permissions are declared in the AndroidManifest.xml file located inside the android/app/src/main/ folder. Each permission is added with a <uses-permission> tag, for example:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

Declaring these permissions lets Android know what your app needs access to, but the user still has to approve them when the app runs.

Note: React Native libraries may automatically include required permissions via manifest merges. Add only what’s not included by your dependencies.

4. Keep your permission list clean

One of the biggest mistakes developers make is adding too many permissions “just in case.” Every permission you include increases the likelihood that users will hesitate or that your app will be flagged during a store review. Always include only the permissions your app truly needs.

Once you have the native setup ready, you can proceed to request and manage permissions in your app’s code.

Configuring Permissions in Expo Projects

If you are using Expo to build your React Native app, managing permissions is simpler because Expo handles most of the native setup for you. Instead of editing native files, you define permissions inside the app.json or app.config.js file. This configuration tells Expo what permissions your app needs during the build process.

1. Adding permissions for Android and iOS

Inside your configuration file, you can specify both Android and iOS permissions like this:

{
  "expo": {
    "android": {
      "permissions": ["android.permission.CAMERA", "android.permission.RECORD_AUDIO"]
    },
    "ios": {
      "infoPlist": {
        "NSCameraUsageDescription": "This app needs camera access to scan QR codes.",
        "NSMicrophoneUsageDescription": "This app needs microphone access for voice input."
      }
    }
  }
}

This approach tells Expo to automatically include the necessary entries in the native Android and iOS project files during the build. It saves you from manually editing those files.

2. Making sure changes apply correctly

If you update any permissions or change their descriptions, remember that these updates only take effect when you rebuild the native app. Running an over-the-air update or simply refreshing the Expo Go app will not apply the changes. To apply new permissions, you need to create a fresh build using EAS Build or the Expo CLI.

3. Keep explanations clear

Just like on iOS native builds, permission messages in Expo should clearly explain why the app needs access. Avoid generic statements like “This app needs access to your camera.” Instead, make it specific to the feature, for example, “We use your camera to capture images for your profile.”

4. Limiting permissions

Expo automatically includes only the permissions you list in your configuration file. This helps prevent unnecessary requests, keeps your app lightweight, and reduces the risk of store rejection.

By using Expo’s configuration system, you can handle permissions in a cleaner, more centralized way, without directly modifying the native files.

Requesting and Checking Runtime Permissions

After you complete the native setup, the next step is to request permissions from users when they actually use a feature. This is known as runtime permission handling, which ensures that your app only requests access when it is truly necessary.

Why runtime permissions matter?

Declaring permissions in configuration files only tells the system what your app might need. But users must still approve those permissions before your app can access that feature. Handling permissions at runtime helps you avoid unexpected crashes and gives users more control.

Checking and requesting permissions

The easiest way to manage permissions across both Android and iOS is by using the react-native-permissions library. It provides two key functions: check and request.

Here’s a simple example of how you can handle camera permission safely:

import { check, request, PERMISSIONS, RESULTS, openSettings } from 'react-native-permissions';
import { Platform, Alert } from 'react-native';

async function handleCameraPermission() {
  const cameraPermission = Platform.OS === 'ios' 
    ? PERMISSIONS.IOS.CAMERA 
    : PERMISSIONS.ANDROID.CAMERA;

  const status = await check(cameraPermission);

  if (status === RESULTS.GRANTED) {
    console.log('Camera access already granted');
    return true;
  }

  if (status === RESULTS.DENIED) {
    const result = await request(cameraPermission);
    return result === RESULTS.GRANTED;
  }

  if (status === RESULTS.BLOCKED) {
    Alert.alert(
      'Permission Required',
      'Camera access is blocked. Please enable it in settings.',
      [{ text: 'Open Settings', onPress: () => openSettings() }]
    );
    return false;
  }

  return false;
}

This example checks whether the camera permission is granted, denied, or blocked. If it’s blocked, the app shows a friendly alert guiding the user to enable it manually from the device settings.

Handling multiple permissions

Sometimes your app needs several permissions at once, such as camera and microphone for a video recording feature. You can request them together in sequence:

import { requestMultiple, PERMISSIONS } from 'react-native-permissions';
import { Platform } from 'react-native';

async function requestMediaPermissions() {
  const permissions = Platform.OS === 'ios' 
    ? [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE]
    : [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.RECORD_AUDIO];

  const statuses = await requestMultiple(permissions);
  console.log('Permission statuses:', statuses);
}

This approach makes it easier to manage related permissions together and ensures smoother user experience.

Handling Web and Testing Permissions

When running React Native apps on the web, permissions such as the camera, microphone, or location work differently from those on mobile platforms. Browsers only allow these permissions in secure contexts, such as https:// or http://localhost. If the site isn’t secure, permission requests will automatically fail.

Web example

To request location permission on the web, you can use the browser’s built-in API:

function getUserLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      (position) => console.log(position.coords),
      (error) => console.log('Permission denied:', error.message)
    );
  }
}

This handles both approval and denial in a simple, reliable way.

Testing permissions

While testing on mobile, you might need to simulate how the app behaves when a user denies or grants a permission. To reset permissions, uninstall and reinstall the app. In Expo Go, you can do this by running:

npx expo start

And relaunching the app using ‘a‘ or’ i‘ from the terminal. For automated tests, mock permission results in Jest to keep test behavior consistent:

jest.mock('react-native-permissions', () => ({
  check: jest.fn(() => Promise.resolve('granted'))
}));

Keeping your test environment predictable ensures stable results and helps you verify that your permission logic works as expected.

Wrapping Up

Handling permissions in React Native comes down to timing, clarity, and testing. Ask for permissions only when necessary, and make your request messages clear so users immediately understand why the app needs access. If a user denies access, don’t block them from continuing; instead, guide them to settings if they wish to enable it later.

Keep your configuration clean and request only what your app truly uses. Fewer permissions mean fewer potential issues during store review and better trust from users. Before releasing your app, always test how it behaves across different scenarios (granted, denied, and blocked permissions) to make sure it performs consistently.

Using a unified library like react-native-permissions simplifies the process and helps your app behave predictably across Android, iOS, and web. For more in-depth reference, visit the React Native Permissions GitHub repository.