Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Photos from the gallery and camera

Phone features such as the camera, location, contacts and vibration are available through Expo modules. Each module is a package with JavaScript functions backed by native iOS and Android code. Let us look at expo-image-picker: the user picks an avatar for the blog profile.

Installing

npx expo install expo-image-picker

The module is already built into Expo Go, so it works right after installing the package.

// app/(tabs)/profile.tsx
import { Image } from 'expo-image';
import * as ImagePicker from 'expo-image-picker';
import { useState } from 'react';
import { Button, View } from 'react-native';
 
export default function Profile() {
  const [avatar, setAvatar] = useState<string | null>(null);
 
  async function pickImage() {
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ['images'],
      allowsEditing: true,
      aspect: [1, 1],
      quality: 0.8,
    });
 
    if (!result.canceled) {
      setAvatar(result.assets[0].uri);
    }
  }
 
  return (
    <View style={{ flex: 1, alignItems: 'center', padding: 16, gap: 12 }}>
      {avatar && <Image source={avatar} style={{ width: 120, height: 120, borderRadius: 60 }} />}
      <Button title="Choose photo" onPress={pickImage} />
    </View>
  );
}

Tap "Choose photo" and the system gallery opens. After you pick a photo, a crop screen to a square appears (aspect: [1, 1]), and then a round avatar above the button. uri is the file address on the phone; you can show it in an Image or upload it to a server.

Options

OptionWhat it sets
mediaTypesWhat to pick: ['images'], ['videos'] or both
allowsEditingShow the crop screen
aspectCrop proportions, for example [4, 3]
qualityCompression quality from 0 to 1

If the user closed the gallery, result.canceled is true and there are no assets.

Taking a photo

The camera needs the user's permission. It is requested before taking a photo:

// app/(tabs)/profile.tsx
async function takePhoto() {
  const permission = await ImagePicker.requestCameraPermissionsAsync();
  if (!permission.granted) {
    Alert.alert('No access to the camera');
    return;
  }
 
  const result = await ImagePicker.launchCameraAsync({ allowsEditing: true, aspect: [1, 1] });
  if (!result.canceled) {
    setAvatar(result.assets[0].uri);
  }
}

Add a <Button title="Take photo" onPress={takePhoto} /> button and Alert to the import. On the first press the system asks for permission, then the camera opens. The iOS Simulator has no camera, so test on a phone.

The permission message

The text of the system dialog is set in app.json through the module's plugin:

{
  "expo": {
    "plugins": [
      ["expo-image-picker", { "cameraPermission": "The camera is used to take a profile photo." }]
    ]
  }
}

This text goes into the app when it is built with EAS; Expo Go shows its own standard text.

Common mistake

Not checking result.canceled and reading result.assets[0].uri right away. If the user closed the gallery without choosing, assets is null, and the app crashes. Always check for cancellation before using the result.

Thanks for reading!

Donate to the site — we work hard for you.