skip to content

With expo-image-picker, what does launchImageLibraryAsync return, and how should a receipt-import flow handle the result?

level: middleimportance: should knowfreq 40%

answer

  1. the system picker UI
  2. canceled plus an assets array
  3. check canceled before assets
  4. uri, width, height, fileName
  5. iOS videos may still prompt

basics

~20 s

launchImageLibraryAsync opens the system picker and resolves with { canceled, assets }. When canceled is false, assets holds objects with uri, width, height and file details; a cancel is a normal outcome, not an error.

solid answer

~40 s

`launchImageLibraryAsync` from `expo-image-picker` opens the platform's own picker UI and resolves with an object holding `canceled` and `assets`. I check `canceled` first; when it is false, `assets` is an array whose items carry `uri`, `width`, `height`, `type`, `fileName`, `fileSize` and `assetId`, plus `base64` and `exif` when requested. Options such as `mediaTypes: ['images']`, `allowsEditing` with `aspect`, `quality` and `allowsMultipleSelection` shape the pick. The docs note that launching the library needs no permission request; the exception is iOS videos returned untouched, where iOS asks for library access after selection, so the docs suggest requesting it up front. The config plugin sets iOS prompt texts and, by default, adds Android's audio permission, which `microphonePermission: false` blocks.

code

tsx · 14 lines
tsx
import * as ImagePicker from 'expo-image-picker';

export async function importReceiptFromLibrary(): Promise<string | null> {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ['images'],
    allowsEditing: true,
    quality: 0.8,
  });

  // Backing out is a normal outcome, not an error.
  if (result.canceled) return null;

  return result.assets[0].uri;
}

go deeper

for a junior

Recall the result shape, canceled and an assets array, and check canceled before reading assets[0].uri.

for a middle

Explain the options a real import uses, why launching the library usually needs no permission, and the iOS video exception since SDK 54.

for a senior

Choose between the system picker and an in-app camera for a feature, and trim native configuration such as the default Android audio permission.

for a principal

Decide which capture and import paths the product supports and what each costs in permissions, store review exposure and support load.

## What the call does `expo-image-picker` opens the **operating system's own picker** rather than drawing its own gallery. `launchImageLibraryAsync(options)` shows the library; `launchCameraAsync` opens the system camera UI. Because the system owns the UI, the app gets back only what the user chose. ## The result shape The promise resolves with an object of two fields: - **`canceled`**: `true` when the user backed out. - **`assets`**: when not canceled, an array of picked items. Each asset carries: | field | meaning | |---|---| | `uri` | a local file uri for the picked or edited file | | `width`, `height` | pixel dimensions | | `type` | `'image'` or `'video'` | | `fileName`, `fileSize` | original name and size | | `assetId` | the library's identifier for the item | | `base64`, `exif` | only filled when requested | | `duration` | for videos | The correct first check is **`if (result.canceled) return`**. A cancel is a normal user choice, so it should not show an error. ## Options a receipt import uses - **`mediaTypes: ['images']`** keeps videos out of the list. - **`allowsEditing: true`** with **`aspect`** lets the user crop to the receipt before import. - **`quality`** from 0 to 1 compresses the result. - **`allowsMultipleSelection`** allows several receipts at once; the result then has several assets. ## Permissions: usually none, with one iOS exception The docs' own example states that **no permission request is necessary to launch the image library**. The exception, documented for SDK 54 and later, is iOS video: 1. By default `allowsEditing` is `false` and `videoExportPreset` is `'Passthrough'`, so the picker returns the **original** asset without compression. 2. iOS requires **media library permission** to access that original file, so a permission dialog appears **right after** the user selects a video. 3. To avoid that surprise, request media library access **before** opening the picker. A receipt import that restricts `mediaTypes` to images does not hit this path. ## Native configuration The package's config plugin changes the native build: - iOS prompt texts through `photosPermission`, `cameraPermission` and `microphonePermission`; - on Android the package adds the **audio recording** permission by default, and setting `microphonePermission` to `false` blocks it, which a photo-only app should do; - `cameraPermission: false` likewise blocks the Android camera permission; - crop toolbar `colors`, including a `dark` variant, for the crop screen. The picker also works in Expo Go, which already contains the package. ## Handling the picked file What the app does next matters as much as the pick: - **Copy or upload promptly**: the `uri` points at a local file the app should not assume is permanent. - **Check `fileSize`** before uploading, and compress with a lower `quality` rather than sending a multi-megabyte original for a receipt. - **Use `width` and `height`** to lay out a preview at the right aspect ratio without waiting for the image to load. - **Request `exif` only when needed**, such as for the capture date, because it enlarges the result. ## Picker or in-app camera? | | `expo-image-picker` | `expo-camera` `CameraView` | |---|---|---| | UI | the system's | your own screen | | control over capture | little | full: overlays, torch, zoom, scanning | | permissions for library picks | usually none | camera permission required | | effort | a few lines | a whole screen | For "import an existing receipt photo", the picker is the right tool; for a guided scan with an outline overlay, `CameraView` is. ## Known iOS quirk The docs record an iOS issue in which, for some high-resolution images, the **cropped rectangle** from `allowsEditing` can come back wrong because of the underlying system picker. If exact crop geometry matters, crop in your own step after the pick.

  • Why does a user sometimes see a photo-library permission prompt after picking a video on iOS?
    Since SDK 54 the defaults return the original video untouched (`allowsEditing: false`, `videoExportPreset: 'Passthrough'`), and iOS needs media library permission to hand over that original file. Requesting media library access before opening the picker avoids the surprise prompt.
  • When is launchCameraAsync a better choice than building a CameraView screen?
    When the app just needs one photo and the system camera UI is acceptable: it is a single call and needs no custom screen. A guided receipt scan with an overlay, torch control or barcode reading needs `CameraView`.

saying these in an interview costs you the question

  • A cancelled pick should be reported to the user as an error.
  • The result is the picked asset itself, not an object with an assets array.
  • Opening the image library always requires requesting media library permission first.
  • expo-image-picker draws its own gallery screen inside the app.
  • allowsEditing crops are always pixel-exact on iOS.