Native Modules & Device APIs

A huge part of what makes React Native practical is that you almost never need to write native Android/iOS code yourself to reach a device feature. The React Native and Expo ecosystems have mature, actively maintained packages for nearly every device capability you can think of — camera, GPS, contacts, biometrics, push notifications, Bluetooth, and more. Each of these packages already contains the native Java/Kotlin and Objective-C/Swift code under the hood; you just install it and call a JavaScript API.

Taking or Picking a Photo

For camera access, two popular choices are expo-camera (for Expo-managed projects) and react-native-image-picker (works in both Expo and bare projects). Both expose a simple async function you call from an event handler; the native camera or photo library UI takes over, and you get a result object back with a local file URI when the user is done.

// Conceptual example using react-native-image-picker
import { launchCamera } from 'react-native-image-picker';
import { Button, Image, View } from 'react-native';
import React, { useState } from 'react';

export default function PhotoPicker() {
  const [photoUri, setPhotoUri] = useState(null);

  const takePhoto = async () => {
    const result = await launchCamera({ mediaType: 'photo', quality: 0.8 });
    if (!result.didCancel && result.assets?.length) {
      setPhotoUri(result.assets[0].uri);
    }
  };

  return (
    <View>
      <Button title="Take Photo" onPress={takePhoto} />
      {photoUri && <Image source={{ uri: photoUri }} style={{ width: 200, height: 200 }} />}
    </View>
  );
}

Getting the Device's GPS Location

For location, Expo projects typically use expo-location, while bare projects commonly use @react-native-community/geolocation. Both follow the same shape: request permission, then call an async function that resolves with the device's current coordinates.

// Conceptual example using expo-location
import * as Location from 'expo-location';

async function getCurrentCoords() {
  const { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== 'granted') {
    throw new Error('Location permission was denied');
  }

  const position = await Location.getCurrentPositionAsync({});
  return {
    latitude: position.coords.latitude,
    longitude: position.coords.longitude,
  };
}

Notice the same shape both examples share: request permission first, check whether it was actually granted, and only then call the sensitive API. That pattern is worth internalizing — it applies to almost every device API, not just camera and location.

The General Pattern: Requesting and Checking Permissions

Modern mobile OSes require apps to explicitly ask the user before touching sensitive hardware or data (camera, location, contacts, microphone, notifications). Skipping the permission check and calling the API anyway typically just fails silently or throws, so always request first, inspect the result, and only proceed on an explicit "granted" status.

import { Camera } from 'expo-camera';

async function ensureCameraPermission() {
  const { status } = await Camera.requestCameraPermissionsAsync();

  if (status !== 'granted') {
    // Show the user an explanation, or a button to open Settings
    return false;
  }

  return true; // safe to open the camera now
}

For a bare React Native project targeting both platforms, react-native-permissions gives you one unified API for checking and requesting almost any OS permission (camera, location, notifications, contacts, and more) instead of juggling each platform's permission APIs separately.

Other Common Device-API Packages

PackageWhat it's for
expo-notificationsRequesting permission for and receiving push/local notifications
@react-native-community/netinfoChecking whether the device is online, and what type of connection it has (wifi/cellular)
react-native-permissionsA single, unified API for checking/requesting almost any OS permission on both iOS and Android
expo-camera / react-native-image-pickerCamera access and photo library picking
expo-location / @react-native-community/geolocationGPS coordinates and location updates
Before reaching for anything custom: search npm/Expo's package directory for your feature first. The community has almost certainly already solved it, tested it against both platforms' quirks, and kept it updated across React Native releases — something a hand-rolled native module would need constant maintenance to match.

When You'd Write Your Own Native Module (Rare)

Occasionally — genuinely rarely — a project needs to talk to a native SDK or platform feature that no existing JavaScript package wraps yet. In the modern React Native architecture, this is done by writing a Turbo Module: a small piece of native Android (Java/Kotlin) and/or iOS (Objective-C/Swift) code that exposes a typed JavaScript interface back to your app.

This is an advanced, specialized skill most React Native app developers never need to touch in their entire career — it belongs more to library authors than app authors. If you ever find yourself considering it, the right first steps are: (1) search harder for an existing package, (2) check if the native SDK's vendor already publishes a React Native wrapper, and only then (3) look into the official React Native documentation on Turbo Modules. This tutorial track intentionally does not attempt to teach native Android/iOS programming — it's a separate discipline with its own learning curve.

Note: All code on this page is a correct, static example meant to be copied into your own project — this site does not run a React Native build, so nothing here executes on the page itself.
Next up: bringing your UI to life with motion — fading views in, animating rotations, and the built-in Animated API. Continue to Animations.