---
title: Use an image picker
description: In this tutorial, learn how to use Expo Image Picker.
hasVideoLink: true
---
React Native provides built-in components as standard building blocks, such as ``, `<Text>`, and `<Pressable>`. We are building a feature to select an image from the device's media gallery. This isn't possible with the core components and we'll need a library to add this feature in our app.
We'll use [`expo-image-picker`](/versions/latest/sdk/imagepicker), a library from Expo SDK.
> `expo-image-picker` provides access to the system's UI to select images and videos from the phone's library.
---
<Step label="1">
## Install expo-image-picker
To install the library, run the following command:
> **info** **Tip:** Any time we install a new library in our project, stop the development server by pressing <kbd>Ctrl</kbd> + <kbd>c</kbd> in the terminal and then run the installation command. After the installation completes, we can start the development server again by running `npx expo start` from the same terminal window.
</Step>
<Step label="2">
## Pick an image from the device's media library
`expo-image-picker` provides `launchImageLibraryAsync()` method to display the system UI by choosing an image or a video from the device's media library. We'll use the primary themed button created in the previous chapter to select an image from the device's media library and create a function to launch the device's image library to implement this functionality.
In **app/(tabs)/index.tsx**, import `expo-image-picker` library and create a `pickImageAsync()` function inside the `Index` component:
{/* prettier-ignore */}
```tsx app/(tabs)/index.tsx|collapseHeight=440
// ...rest of the import statements remain unchanged
/* @tutinfo Import <CODE>ImagePicker</CODE>. */ import * as ImagePicker from 'expo-image-picker';/* @end */
/* @tutinfo Pass image picker options to <CODE>launchImageLibraryAsync()</CODE> */
const pickImageAsync = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 1,
});
if (!result.canceled) {
/* @tutinfo If the image is picked, print its information in terminal window. */
console.log(result);
/* @end */
} else {
/* @tutinfo If the user does not pick an image, show an alert. */
alert('You did not select any image.');
/* @end */
}
};
/* @end */
// ...rest of the code remains same
}
```
Let's learn what the above code does:
- The `launchImageLibraryAsync()` receives an object to specify different options. This object is the [`ImagePickerOptions`](/versions/latest/sdk/imagepicker/#imagepickeroptions) object, which we are passing when invoking the method.
- When `allowsEditing` is set to `true`, the user can crop the image during the selection process on Android and iOS.
</Step>
<Step label="3">
## Update the button component
On pressing the primary button, we'll call the `pickImageAsync()` function on the `Button` component. Update the `onPress` prop of the `Button` component in **components/Button.tsx**:
{/* prettier-ignore */}
```tsx components/Button.tsx
type Props = {
label: string;
theme?: 'primary';
/* @tutinfo Define the type for <CODE>onPress</CODE> prop. */
onPress?: () => void;
/* @end */
};
if (theme === 'primary') {
return (
<View
style={[
styles.buttonContainer,
{ borderWidth: 4, borderColor: '#ffd33d', borderRadius: 18 },
]}>
<Pressable style={[styles.button, { backgroundColor: '#fff' }]} onPress={/* @tutinfo */onPress/* @end */}>
<Text style={[styles.buttonLabel, { color: '#25292e' }]}>{label}</Text>
</Pressable>
);
}
return (
<Pressable style={styles.button} onPress={() => alert('You pressed a button.')}>
<Text style={styles.buttonLabel}>{label}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
buttonContainer: {
width: 320,
height: 68,
marginHorizontal: 20,
alignItems: 'center',
justifyContent: 'center',
padding: 3,
},
button: {
borderRadius: 10,
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
},
buttonIcon: {
paddingRight: 8,
},
buttonLabel: {
color: '#fff',
fontSize: 16,
},
});
```
In **app/(tabs)/index.tsx**, add the `pickImageAsync()` function to the `onPress` prop on the first `<Button>`.
{/* prettier-ignore */}
```tsx app/(tabs)/index.tsx
const PlaceholderImage = require('@/assets/images/background-image.png');
const pickImageAsync = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 1,
});
if (!result.canceled) {
console.log(result);
} else {
alert('You did not select any image.');
}
};
return (
<View style={styles.imageContainer}>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
alignItems: 'center',
},
imageContainer: {
flex: 1,
},
footerContainer: {
flex: 1 / 3,
alignItems: 'center',
},
});
```
The `pickImageAsync()` function invokes `ImagePicker.launchImageLibraryAsync()` and then handles the result. The `launchImageLibraryAsync()` method returns an object containing information about the selected image.
Here is an example of the `result` object and the properties it contains:
<Tab label="Android">
```json collapseHeight=280
{
"assets": [
{
"assetId": null,
"base64": null,
"duration": null,
"exif": null,
"fileName": "ea574eaa-f332-44a7-85b7-99704c22b402.jpeg",
"fileSize": 4513577,
"height": 4570,
"mimeType": "image/jpeg",
"rotation": null,
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FStickerSmash-13f21121-fc9d-4ec6-bf89-bf7d6165eb69/ImagePicker/ea574eaa-f332-44a7-85b7-99704c22b402.jpeg",
"width": 2854
}
],
"canceled": false
}
```
</Tab>
<Tab label="iOS">
```json collapseHeight=280
{
"assets": [
{
"assetId": "99D53A1F-FEEF-40E1-8BB3-7DD55A43C8B7/L0/001",
"base64": null,
"duration": null,
"exif": null,
"fileName": "IMG_0004.JPG",
"fileSize": 2548364,
"height": 1669,
"mimeType": "image/jpeg",
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FStickerSmash-13f21121-fc9d-4ec6-bf89-bf7d6165eb69/ImagePicker/ea574eaa-f332-44a7-85b7-99704c22b402.jpeg",
"width": 1668
}
],
"canceled": false
}
```
</Tab>
<Tab label="web">
```json
{
"assets": [
{
"fileName": "some-image.png",
"height": 720,
"mimeType": "image/png",
"uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAA"
}
],
"canceled": false
}
```
</Tab>
</Step>
## Use the selected image
The `result` object provides the `assets` array, which contains the `uri` of the selected image. Let's take this value from the image picker and use it to show the selected image in the app.
Modify the **app/(tabs)/index.tsx** file:
1. Declare a state variable called `selectedImage` using the [`useState`](https://react.dev/learn/state-a-components-memory#adding-a-state-variable) hook from React. We'll use this state variable to hold the URI of the selected image.
2. Update the `pickImageAsync()` function to save the image URI in the `selectedImage` state variable.
3. Pass the `selectedImage` as a prop to the `ImageViewer` component.
{/* prettier-ignore */}
```tsx app/(tabs)/index.tsx
/* @tutinfo Import <CODE>useState</CODE> hook from <CODE>react<CODE>.*/
/* @end */
const PlaceholderImage = require('@/assets/images/background-image.png');
/* @tutinfo Create a state variable that will hold the value of selected image. */
const [selectedImage, setSelectedImage] = useState<string | undefined>(undefined);
/* @end */
const pickImageAsync = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 1,
});
if (!result.canceled) {
/* @tutinfo Pick the first uri from the <CODE>assets</CODE> array. Also, there is only one image selected at a time so you don't have to change this. */
setSelectedImage(result.assets[0].uri);
/* @end */
} else {
alert('You did not select any image.');
}
};
return (
<View style={styles.container}>
<View style={styles.imageContainer}>
/* @tutinfo Pass the selected image URI to the <CODE>ImageViewer</CODE> component. */
/* @end */
</View>
<View style={styles.footerContainer}>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
alignItems: 'center',
},
imageContainer: {
flex: 1,
},
footerContainer: {
flex: 1 / 3,
alignItems: 'center',
},
});
```
Pass the `selectedImage` prop to the `ImageViewer` component to display the selected image instead of a placeholder image.
1. Modify the **components/ImageViewer.tsx** file to accept the `selectedImage` prop.
2. The source of the image is getting long, so let's also move it to a separate variable called `imageSource`.
3. Pass `imageSource` as the value of the `source` prop on the `Image` component.
{/* prettier-ignore */}
```tsx components/ImageViewer.tsx
type Props = {
imgSource: ImageSourcePropType;
/* @tutinfo */
selectedImage?: string;
/* @end */
};
/* @tutinfo If the selected image is not null, show the image from the device, otherwise, show the placeholder image. */
const imageSource = selectedImage ? { uri: selectedImage } : imgSource;
/* @end */
/* @tutinfo <CODE>imgSource</CODE> replaced by <CODE>imageSource</CODE>. */
return ;
/* @end */
}
const styles = StyleSheet.create({
image: {
width: 320,
height: 440,
borderRadius: 18,
},
});
```
In the above snippet, the Image component uses a conditional operator to load the image's source. The picked image is a [`uri` string](https://reactnative.dev/docs/images#network-images), not a local asset like the placeholder image.
Let's take a look at our app now:
> The images used for the example app in this tutorial were picked from [Unsplash](https://unsplash.com).
## Summary