expo-image-picker
Version: 55.0.6
Provides access to the system’s UI for selecting images and videos from the phone’s library or taking a photo with the camera. Supports image editing, multiple selection, and customizable media types.
Installation
npx expo install expo-image-picker
Usage
import * as ImagePicker from 'expo-image-picker';
import { Button, Image, View } from 'react-native';
import { useState } from 'react';
export default function App() {
const [image, setImage] = useState<string | null>(null);
const pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
setImage(result.assets[0].uri);
}
};
return (
<View>
<Button title="Pick Image" onPress={pickImage} />
{image && <Image source={{ uri: image }} style={{ width: 200, height: 200 }} />}
</View>
);
}
API Reference
Methods
launchImageLibraryAsync(options)
(options?: ImagePickerOptions) => Promise<ImagePickerResult>
Opens the system UI for choosing an image or video from the libraryconst result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [16, 9],
quality: 0.8,
});
launchCameraAsync(options)
(options?: ImagePickerOptions) => Promise<ImagePickerResult>
Opens the system camera for taking a photo or videoconst result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 1,
});
requestCameraPermissionsAsync()
() => Promise<CameraPermissionResponse>
Requests camera permissions from the userconst { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status === 'granted') {
// Permission granted
}
getCameraPermissionsAsync()
() => Promise<CameraPermissionResponse>
Checks current camera permission statusconst { status } = await ImagePicker.getCameraPermissionsAsync();
requestMediaLibraryPermissionsAsync(writeOnly)
(writeOnly?: boolean) => Promise<MediaLibraryPermissionResponse>
Requests media library permissionsParameters:
writeOnly (boolean): Request write-only permissions. Defaults to false
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
getMediaLibraryPermissionsAsync(writeOnly)
(writeOnly?: boolean) => Promise<MediaLibraryPermissionResponse>
Checks current media library permission status
getPendingResultAsync()
() => Promise<ImagePickerResult | null>
Retrieves pending result on Android when MainActivity is destroyedconst result = await ImagePicker.getPendingResultAsync();
if (result) {
// Handle recovered result
}
Hooks
useCameraPermissions(options)
() => [PermissionResponse | null, () => Promise<PermissionResponse>, () => Promise<PermissionResponse>]
Hook for camera permissionsconst [permission, requestPermission] = ImagePicker.useCameraPermissions();
if (!permission?.granted) {
await requestPermission();
}
useMediaLibraryPermissions(options)
(options?: { writeOnly?: boolean }) => [PermissionResponse | null, () => Promise<PermissionResponse>, () => Promise<PermissionResponse>]
Hook for media library permissionsconst [permission, requestPermission] = ImagePicker.useMediaLibraryPermissions();
Types
ImagePickerOptions
Type of media to select. Values: All, Images, Videos
Whether to show editing UI after selection. Defaults to false
Allow selecting multiple items. Defaults to false
Aspect ratio for editing (e.g., [4, 3])
Image quality (0 to 1). Defaults to 0.2
Maximum video duration in seconds
videoQuality
UIImagePickerControllerQualityType
Video quality: 0 (High), 1 (Medium), 2 (Low)
Include EXIF data. Defaults to false
Include base64 representation. Defaults to false
ImagePickerResult
type ImagePickerResult =
| {
canceled: false;
assets: ImagePickerAsset[];
}
| {
canceled: true;
assets: null;
};
ImagePickerAsset
URI of the selected media
EXIF metadata (if requested)
Base64 data (if requested)
Video duration in milliseconds
Enums
enum MediaTypeOptions {
All = 'All',
Images = 'Images',
Videos = 'Videos',
}
Examples
Pick Image from Library
import * as ImagePicker from 'expo-image-picker';
async function selectImage() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
});
if (!result.canceled) {
const asset = result.assets[0];
console.log('Image URI:', asset.uri);
console.log('Dimensions:', asset.width, 'x', asset.height);
}
}
Take Photo with Camera
import * as ImagePicker from 'expo-image-picker';
async function takePhoto() {
// Request permission first
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status !== 'granted') {
alert('Camera permission required');
return;
}
const result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
console.log('Photo taken:', result.assets[0].uri);
}
}
Select Multiple Images
import * as ImagePicker from 'expo-image-picker';
async function selectMultiple() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsMultipleSelection: true,
quality: 0.8,
});
if (!result.canceled) {
console.log(`Selected ${result.assets.length} images`);
result.assets.forEach((asset) => {
console.log(asset.uri);
});
}
}
Pick Video with Editing
import * as ImagePicker from 'expo-image-picker';
async function selectVideo() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Videos,
allowsEditing: true,
videoMaxDuration: 60, // 60 seconds max
videoQuality: ImagePicker.UIImagePickerControllerQualityType.High,
});
if (!result.canceled) {
const video = result.assets[0];
console.log('Video URI:', video.uri);
console.log('Duration:', video.duration, 'ms');
}
}
Get Image with EXIF Data
import * as ImagePicker from 'expo-image-picker';
async function getImageWithExif() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
exif: true,
});
if (!result.canceled && result.assets[0].exif) {
const exif = result.assets[0].exif;
console.log('GPS:', exif.GPSLatitude, exif.GPSLongitude);
console.log('Camera:', exif.Make, exif.Model);
console.log('Date:', exif.DateTime);
}
}
Complete Example with Permissions
import * as ImagePicker from 'expo-image-picker';
import { useState } from 'react';
import { View, Button, Image, StyleSheet, Text } from 'react-native';
export default function ImagePickerExample() {
const [image, setImage] = useState<string | null>(null);
const [cameraPermission, requestCameraPermission] =
ImagePicker.useCameraPermissions();
const [libraryPermission, requestLibraryPermission] =
ImagePicker.useMediaLibraryPermissions();
const takePhoto = async () => {
if (!cameraPermission?.granted) {
const result = await requestCameraPermission();
if (!result.granted) return;
}
const result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
setImage(result.assets[0].uri);
}
};
const pickImage = async () => {
if (!libraryPermission?.granted) {
const result = await requestLibraryPermission();
if (!result.granted) return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
setImage(result.assets[0].uri);
}
};
return (
<View style={styles.container}>
<Button title="Take Photo" onPress={takePhoto} />
<Button title="Pick Image" onPress={pickImage} />
{image && (
<Image source={{ uri: image }} style={styles.image} />
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, gap: 10 },
image: { width: 300, height: 300, marginTop: 20 },
});
| Platform | Supported |
|---|
| iOS | ✅ |
| Android | ✅ |
| Web | ✅ |
Permissions
Camera and media library permissions are required.
iOS
Add to app.json:
{
"expo": {
"plugins": [
[
"expo-image-picker",
{
"photosPermission": "Allow $(PRODUCT_NAME) to access your photos.",
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera."
}
]
]
}
}
Android
Permissions are automatically added:
READ_EXTERNAL_STORAGE / READ_MEDIA_IMAGES / READ_MEDIA_VIDEO
WRITE_EXTERNAL_STORAGE (Android < 10)
CAMERA
Best Practices
- Always Request Permissions: Check and request permissions before launching picker
- Quality Settings: Use
quality: 1 for profile photos, lower for thumbnails
- Handle Cancellation: Always check
result.canceled before accessing assets
- Memory Management: Be cautious with
base64 on large images
- Multiple Selection: Don’t use
allowsEditing with allowsMultipleSelection
On Android, if the system kills the activity, use getPendingResultAsync() to recover the selection. Test by enabling “Don’t keep activities” in developer options.
Resources