or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdindex.mdmedia-selection.mdpermissions.md
tile.json

media-selection.mddocs/

Media Selection

Core functions for capturing images and videos with the device camera or selecting media from the device's photo library.

Capabilities

Launch Camera

Display the system UI for taking a photo with the camera. Requires camera permissions and media library permissions on iOS 10.

/**
 * Display the system UI for taking a photo with the camera.
 * @param options Configuration options for camera capture
 * @returns Promise resolving to ImagePickerResult with captured media or cancellation
 */
function launchCameraAsync(options?: ImagePickerOptions): Promise<ImagePickerResult>;

Platform Notes:

  • Web: Must be called immediately in a user interaction (button press) or browser will block
  • Android: Handle MainActivity destruction using getPendingResultAsync
  • iOS: Requires camera permissions and media library permissions on iOS 10

Usage Example:

import * as ImagePicker from 'expo-image-picker';

const takePicture = async () => {
  // Check permissions first
  const { status } = await ImagePicker.requestCameraPermissionsAsync();
  if (status !== 'granted') {
    alert('Camera permission is required!');
    return;
  }

  // Launch camera
  const result = await ImagePicker.launchCameraAsync({
    mediaTypes: 'images',
    allowsEditing: true,
    aspect: [1, 1],
    quality: 0.8,
  });

  if (!result.canceled) {
    console.log('Photo taken:', result.assets[0]);
  }
};

Launch Image Library

Display the system UI for choosing media from the phone's library. Requires media library permissions on iOS 10 only.

/**
 * Display the system UI for choosing an image or video from the phone's library.
 * @param options Configuration options for media selection
 * @returns Promise resolving to ImagePickerResult with selected media or cancellation
 */
function launchImageLibraryAsync(options?: ImagePickerOptions): Promise<ImagePickerResult>;

Platform Notes:

  • Web: Must be called immediately in a user interaction (button press) or browser will block
  • Android: Animated GIFs preserved only with quality: 1.0 and allowsEditing: false
  • iOS: Supports GIF quality and cropping

Usage Examples:

import * as ImagePicker from 'expo-image-picker';

// Single image selection
const pickImage = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: 'images',
    allowsEditing: true,
    aspect: [4, 3],
    quality: 1,
  });

  if (!result.canceled) {
    console.log('Selected image:', result.assets[0].uri);
  }
};

// Multiple selection
const pickMultipleImages = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: 'images',
    allowsMultipleSelection: true,
    selectionLimit: 5,
    quality: 0.8,
  });

  if (!result.canceled) {
    console.log(`Selected ${result.assets.length} images`);
    result.assets.forEach((asset, index) => {
      console.log(`Image ${index + 1}:`, asset.uri);
    });
  }
};

// Video selection with editing
const pickVideo = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: 'videos',
    allowsEditing: true,
    videoQuality: ImagePicker.UIImagePickerControllerQualityType.Medium,
  });

  if (!result.canceled) {
    const video = result.assets[0];
    console.log('Selected video:', video.uri);
    console.log('Duration:', video.duration, 'ms');
  }
};

// Live Photos (iOS only)
const pickLivePhoto = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ['images', 'livePhotos'],
    quality: 1,
  });

  if (!result.canceled) {
    const asset = result.assets[0];
    if (asset.type === 'livePhoto' && asset.pairedVideoAsset) {
      console.log('Live photo image:', asset.uri);
      console.log('Paired video:', asset.pairedVideoAsset.uri);
    }
  }
};

Get Pending Result

Retrieve lost data on Android when MainActivity is destroyed after ImagePicker finishes.

/**
 * Android system sometimes kills the MainActivity after ImagePicker finishes.
 * This function retrieves the lost data from a previous picker operation.
 * @returns Promise resolving to the previous ImagePicker result or null
 */
function getPendingResultAsync(): Promise<ImagePickerResult | ImagePickerErrorResult | null>;

Platform Notes:

  • Android: Returns the lost ImagePicker result if MainActivity was destroyed
  • Other platforms: Always returns null

Usage Example:

import * as ImagePicker from 'expo-image-picker';
import { AppState } from 'react-native';

// Check for pending result when app becomes active
useEffect(() => {
  const handleAppStateChange = async (nextAppState: string) => {
    if (nextAppState === 'active') {
      const pendingResult = await ImagePicker.getPendingResultAsync();
      if (pendingResult && !pendingResult.canceled) {
        // Handle the recovered result
        console.log('Recovered result:', pendingResult.assets);
      }
    }
  };

  const subscription = AppState.addEventListener('change', handleAppStateChange);
  return () => subscription?.remove();
}, []);

Result Types

// Main result type
type ImagePickerResult = ImagePickerSuccessResult | ImagePickerCanceledResult;

interface ImagePickerSuccessResult {
  canceled: false;
  assets: ImagePickerAsset[];
}

interface ImagePickerCanceledResult {
  canceled: true;
  assets: null;
}

// Error result (from getPendingResultAsync)
interface ImagePickerErrorResult {
  code: string;
  message: string;
  exception?: string;
}

// Asset information
interface ImagePickerAsset {
  uri: string;
  width: number;
  height: number;
  type?: 'image' | 'video' | 'livePhoto' | 'pairedVideo';
  assetId?: string | null;
  fileName?: string | null;
  fileSize?: number;
  base64?: string | null;
  exif?: Record<string, any> | null;
  duration?: number | null;
  mimeType?: string;
  pairedVideoAsset?: ImagePickerAsset | null; // iOS Live Photos
  file?: File; // Web only
}