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
| Package | What it's for |
|---|---|
expo-notifications | Requesting permission for and receiving push/local notifications |
@react-native-community/netinfo | Checking whether the device is online, and what type of connection it has (wifi/cellular) |
react-native-permissions | A single, unified API for checking/requesting almost any OS permission on both iOS and Android |
expo-camera / react-native-image-picker | Camera access and photo library picking |
expo-location / @react-native-community/geolocation | GPS coordinates and location updates |
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.
Animated API. Continue to Animations.