Skip to main content
React Native Introduction
CHAPTER 24 Beginner

Camera, Gallery, and Device Features

Updated: May 16, 2026
7 min read

# CHAPTER 24

1. Introduction

A website runs in a sandbox; it is isolated from the computer. A mobile app lives intimately with the hardware. Users expect mobile apps to utilize the camera to scan QR codes, the gallery to upload avatars, and the GPS chip to track deliveries. In traditional native development, accessing this hardware requires complex Java intents and Swift delegates. In React Native, Expo provides unified, abstracted APIs that make hardware access trivial. In this chapter, we will master Camera, Gallery, and Device Features. We will manage strict OS permissions and utilize expo-image-picker to capture and render media.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Install and configure hardware-specific Expo packages.
  • Request Camera and Media Library permissions safely.
  • Launch the native device Camera to take a photograph.
  • Launch the native Photo Gallery to select an image.
  • Render the captured image URI dynamically in the UI.

3. The Permissions Barrier

Apple and Google are incredibly strict about privacy. Your app cannot secretly turn on the camera. Whenever you attempt to access hardware (Camera, Microphone, GPS, Gallery), you MUST execute an asynchronous function requesting permission. If the user clicks "Deny" on the popup, the OS blocks your app permanently until the user manually changes it in their settings. You must handle denials gracefully!

4. Installation

We will use the highly reliable Expo Image Picker library, which handles both taking photos and selecting them from the gallery.
bash
1
npx expo install expo-image-picker
Let's build a component with a button that opens the user's camera roll.
javascript
1234567891011121314151617181920212223242526272829303132333435363738394041424344
import React, { useState } from 'react';
import { Button, Image, View, Text } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

export default function GalleryPicker() {
  // State to hold the temporary local file path of the selected image
  const [imageUri, setImageUri] = useState(null);

  const pickImage = async () => {
    // 1. Request Permission to view the gallery
    const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
    
    if (permissionResult.granted === false) {
      alert("Permission to access camera roll is required!");
      return; // Stop execution!
    }

    // 2. Launch the Image Picker UI
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true, // Lets the user crop the photo into a square!
      aspect: [1, 1],
      quality: 0.5, // Compresses the image size by 50%
    });

    // 3. If they didn't hit 'Cancel', save the URI to state!
    if (!result.canceled) {
      setImageUri(result.assets[0].uri);
    }
  };

  return (
    <View style={{ flex: 1, alignItems: &#039;center&#039;, justifyContent: &#039;center&#039; }}>
      <Button title="Pick an image from camera roll" onPress={pickImage} />
      
      {/* 4. Render the image if the state exists! */}
      {imageUri ? (
        <Image source={{ uri: imageUri }} style={{ width: 200, height: 200, marginTop: 20, borderRadius: 100 }} />
      ) : (
        <Text style={{ marginTop: 20 }}>No image selected</Text>
      )}
    </View>
  );
}

6. Taking a Live Photo (The Camera)

The logic is almost perfectly identical to the Gallery code! We simply change the permission requested, and invoke the camera launcher instead.
javascript
1234567891011121314151617181920
const takePhoto = async () => {
  // 1. Request Camera Permission
  const permissionResult = await ImagePicker.requestCameraPermissionsAsync();
  
  if (permissionResult.granted === false) {
    alert("You&#039;ve refused to allow this app to access your camera!");
    return;
  }

  // 2. Launch the Native Camera ViewFinder
  const result = await ImagePicker.launchCameraAsync({
    allowsEditing: true,
    aspect: [4, 3],
    quality: 0.8,
  });

  if (!result.canceled) {
    setImageUri(result.assets[0].uri); // Saves to the exact same state!
  }
};

7. Preparing to Upload (FormData)

Displaying the image locally is nice, but usually, you want to upload it to an AWS S3 bucket or a Node.js server. The URI generated by ImagePicker looks like this: file:///data/user/0/app/cache/ImagePicker/image.jpg. To upload a physical file via Axios, you cannot send standard JSON. You MUST use a FormData object.
javascript
12345678910111213141516171819
// Advanced snippet for uploading to a server
const uploadImage = async (uri) => {
  const formData = new FormData();
  
  // Append the raw file data
  formData.append(&#039;profile_photo&#039;, {
    uri: uri,
    name: &#039;photo.jpg&#039;,
    type: &#039;image/jpeg&#039;,
  });

  try {
    await axios.post(&#039;https://api.example.com/upload', formData, {
      headers: { &#039;Content-Type&#039;: &#039;multipart/form-data&#039; },
    });
  } catch (err) {
    console.error("Upload failed");
  }
};

8. Visual Learning: The Hardware Flow

txt
1234567891011
[ Button Click ]
       |
[ OS Popup: "Allow access to Camera?" ]
      /             \
 (Deny)             (Allow)
   |                  |
(Alert UI)      [ Opens ViewFinder ]
                      |
                 (User snaps photo)
                      |
            [ result.assets[0].uri ] -> Passed to State -> Renders on Screen

9. Common Mistakes

  • Testing Camera on Simulators: The iOS Simulator does NOT have a camera. If you try to run launchCameraAsync on a Mac simulator, the app will crash or do nothing. You MUST test camera functionality using the Expo Go app on a physical device with an actual camera lens!

10. Best Practices

  • Explaining Why: Apple frequently rejects apps from the App Store if they request camera permissions without a valid reason. In your app.json file, Expo allows you to configure permission messages. You must explicitly state: "cameraPermission": "Allow this app to access your camera to update your profile avatar."

11. Practice Exercises

  1. 1. What asynchronous method provided by expo-image-picker triggers the native iOS/Android camera viewfinder?
  1. 2. What specific JavaScript payload format must be utilized when transmitting a physical binary file (like a photo) via an Axios POST request?

12. MCQs with Answers

Question 1

Before an application attempts to execute ImagePicker.launchCameraAsync(), what critical programmatic step is legally and functionally required by the mobile operating system?

Question 2

When a user successfully selects a photo from their gallery using Expo Image Picker, what specific data format does the package return that allows the <Image> component to render it on the screen?

13. Interview Questions

  • Q: Explain the strict security sandbox model employed by iOS and Android regarding hardware access. Why is graceful error handling mandatory when a user denies permission?
  • Q: Describe the architectural difference between a standard JSON payload and a FormData payload when executing an HTTP POST request. Why is FormData required for media uploads?
  • Q: Walk through the specific configuration steps required to ensure your application passes the Apple App Store review process regarding privacy disclosure strings for Camera and Microphone usage.

14. FAQs

Q: Can I build a custom camera view with a massive red record button, like Snapchat or TikTok? A: Yes! expo-image-picker just launches the standard native camera. If you want to build a fully custom UI overlaid on top of the live camera feed, you must install the expo-camera package, which renders the raw lens feed directly into a React Native View!

15. Summary

In Chapter 24, our application broke out of the software sandbox and interfaced directly with the physical world. We navigated the strict privacy architectures of iOS and Android by gracefully requesting explicit hardware permissions. We utilized the versatile expo-image-picker to successfully launch the native Camera ViewFinder and the Photo Gallery UI. Finally, we captured the resulting temporary file URIs, injecting them into React State to dynamically render high-quality user photography directly onto the glass.

16. Next Chapter Recommendation

Our app is highly functional, but to make it feel truly premium, we need to add a layer of polish. Proceed to Chapter 25: Animations in React Native.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·