How-to guides

Add a Photo Picker to an Expo App

Use Expo ImagePicker to open the phone’s photo chooser, keep the selected image URI and show a preview. Picking a photo, uploading it and saving it to a gallery are separate operations.

Updated 28 October 2026

What you will build

An edit-profile screen with Choose photo, a selected-image preview and Remove photo. Cancelling the picker leaves the current selection alone. The example keeps the preview on this screen; saving an avatar to an account needs an upload and a durable server-side reference.

  1. Step 1Choose an image
  2. Step 2Preview the selection
  3. Step 3Save or remove it

This example uses the Expo ImagePicker API documented for SDK 58 and selects images only. Camera capture, video picking and direct photo-library access have different permission requirements. Use the API reference for your project’s SDK.

Build it in Newly

Open your project in Newly and describe the behavior you want. Use this suggested prompt as a starting point, then review the generated changes and follow the checks below.

Suggested prompt
Add an avatar picker to the edit-profile screen using expo-image-picker. Let the user select one image from the system photo picker and show a preview with Replace and Remove controls. Keep the existing avatar if the user cancels. Show a recoverable error if selection fails. Do not request camera, microphone or full-library access just to choose a still image. Keep the local preview separate from the account’s saved avatar; only update the profile after a successful upload.

  1. 1. Choose the interaction before adding permissions

    Ask Newly for a still-image chooser first. Keep camera capture and video out of the initial flow. The system picker lets the person choose an asset without giving the app an unrestricted view of the library. Review the generated app configuration so it does not declare unrelated camera or microphone use.

  2. 2. Handle selection and cancellation separately

    Call launchImageLibraryAsync from the Choose photo button. Check result.canceled before reading result.assets. When the person cancels, keep the existing avatar. When they select a photo, show its URI in a preview and let them replace or remove it before saving.

  3. 3. Finish the save path

    A local URI is not a permanent account avatar. Upload the selected file through your app’s existing storage flow, show progress and errors, and save the returned durable reference only after the upload succeeds. Reopen the profile after an app restart to check the saved image rather than relying on component state.

Expo implementation example

Install the dependencies in your Expo project. Expo resolves native package versions for your installed SDK.

Install command
npx expo install expo-image-picker

Code example
import { useRef, useState } from "react";
import { Button, Image, Text, View } from "react-native";
import * as ImagePicker from "expo-image-picker";

export default function AvatarPicker() {
  const [uri, setUri] = useState<string | null>(null);
  const [message, setMessage] = useState("");
  const [busy, setBusy] = useState(false);
  const picking = useRef(false);

  async function choosePhoto() {
    if (picking.current) return;
    picking.current = true;
    setBusy(true);
    setMessage("");
    try {
      const result = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ["images"],
        allowsEditing: false,
        quality: 0.8,
      });
      if (!result.canceled) {
        setUri(result.assets[0].uri);
        setMessage("Photo selected. Save it to your account when ready.");
      }
    } catch {
      setMessage("Could not open the photo picker. Please try again.");
    } finally {
      picking.current = false;
      setBusy(false);
    }
  }

  return (
    <View style={{ gap: 16, padding: 24 }}>
      <Button title={uri ? "Replace photo" : "Choose photo"}
        onPress={choosePhoto} disabled={busy} />
      {uri ? (
        <>
          <Image source={{ uri }} accessibilityLabel="Selected photo preview"
            style={{ width: 200, height: 200, borderRadius: 16 }} />
          <Button title="Remove photo" onPress={() => {
            setUri(null);
            setMessage("Selection removed.");
          }} disabled={busy} />
        </>
      ) : null}
      <Text accessibilityLiveRegion="polite">{message}</Text>
    </View>
  );
}

This is the selection and preview portion of the feature. It does not upload a file or persist an avatar. Integrate those operations with your app’s storage and authentication before treating Save as complete.

Test before publishing

  • Choose a photo, replace it and remove it; each action leaves a predictable preview.
  • Cancel after an avatar is already selected; the previous selection remains.
  • Try a large image and a cloud-backed image on a device with a slow connection.
  • Verify permission prompts on a fresh installation, rather than relying on permissions granted during development.
  • After adding upload and persistence, close and reopen the app and confirm that the saved avatar loads.

Run these checks in your own project on iOS and Android. A browser preview does not establish native permission, hardware or background behavior.

Common problems

The photo disappears after reopening the screen

The example stores a local preview in React state. Add durable upload and profile persistence; do not save a temporary picker URI as the account’s permanent image.

A permission prompt appears when picking a video

Video uses a different path. Expo documents an iOS media-library permission requirement for the original-video passthrough flow. Keep the still-image flow separate and inspect the options for your installed SDK.

Questions about a photo library picker

Does choosing a photo require full photo-library access?

For the modern system still-image picker, selecting an asset is different from granting unrestricted library access. Direct library reads, camera capture and some video paths have separate requirements. Request only the access needed by the operation your app actually performs.

Does the picker upload the photo?

No. The result contains asset information and a local URI. Uploading the file, storing a durable reference and associating it with the user’s account are separate steps.

Can the user crop the selected photo?

Expo ImagePicker provides allowsEditing for a single selection, with different editing behavior on iOS and Android. Confirm the resulting crop and file format on each platform. The example deliberately starts with editing disabled.

What should happen when the user cancels?

Check result.canceled and leave the previous selection unchanged. Cancellation is an ordinary exit from the picker, so it should not clear an existing avatar or display an error.

Sources and API references

Keep building

Build this feature in Newly