xref: /expo/docs/pages/tutorial/image-picker.mdx (revision fe7fed70)
1---
2title: Use an image picker
3---
4
5import { SnackInline, Terminal } from '~/ui/components/Snippet';
6import Video from '~/components/plugins/Video';
7import { A } from '~/ui/components/Text';
8import { Step } from '~/ui/components/Step';
9import { BoxLink } from '~/ui/components/BoxLink';
10import { BookOpen02Icon } from '@expo/styleguide-icons';
11
12React Native provides built-in components that are standard building blocks used by every application, such as `<View>`, `<Text>`, and `<Pressable>`.
13We want to build a feature that isn't possible with these core components and API: selecting an image from the device's media library. For that, we will need a library.
14
15To achieve this, we'll use an Expo SDK library called <A href="/versions/latest/sdk/imagepicker">`expo-image-picker`</A>.
16
17> `expo-image-picker` provides access to the system's UI to select images and videos from the phone's library or take a photo with the camera.
18
19<Step label="1">
20
21## Install expo-image-picker
22
23To install the library, run the following command:
24
25<Terminal cmd={['$ npx expo install expo-image-picker']} />
26
27> **Tip:** Any time we install a new library in our project, we must stop the development server by pressing <kbd>Ctrl</kbd> + <kbd>c</kbd> in the terminal and then running the installation command.
28> After the installation, we can start the development server again by running `npx expo start` from the same terminal window.
29
30</Step>
31
32<Step label="2">
33
34## Pick an image from the device's media library
35
36`expo-image-picker` provides the `launchImageLibraryAsync()` method that displays the system UI for choosing an image or a video from the device's media library.
37
38We can use the button with the primary theme we created in the previous chapter to pick an image from the device's media library.
39We'll create a function to launch the device's image library to implement this functionality.
40
41In **App.js**, import the `expo-image-picker` library and create a `pickImageAsync()` function inside the `App` component:
42
43{/* prettier-ignore */}
44```jsx App.js
45// ...rest of the import statements remain unchanged
46/* @info Import the ImagePicker. */ import * as ImagePicker from 'expo-image-picker'; /* @end */
47
48export default function App() {
49  const pickImageAsync = async () => {
50    /* @info Pass image picker options to launchImageLibraryAsync() */
51    let result = await ImagePicker.launchImageLibraryAsync({
52      allowsEditing: true,
53      quality: 1,
54    });
55    /* @end */
56
57    if (!result.canceled) {
58      /* @info If the image is picked, print its information in terminal window. */
59      console.log(result);
60      /* @end */
61    } else {
62      /* @info If the user does not picks an image, show an alert. */
63      alert('You did not select any image.');
64      /* @end */
65    }
66  };
67
68  // ...rest of the code remains same
69}
70```
71
72Let's learn what the above code does.
73
74- The `launchImageLibraryAsync()` receives an object in which different options are specified.
75  This object is an <A href="/versions/latest/sdk/imagepicker/#imagepickeroptions">`ImagePickerOptions` object</A>.
76  We can pass the object to specify different options when invoking the method.
77- When `allowsEditing` is set to `true`, the user can crop the image during the selection process on Android and iOS but not on the web.
78
79</Step>
80
81<Step label="3">
82
83## Update the button component
84
85When the primary button gets pressed, we need to call the `pickImageAsync()` function.
86To call it, update the `onPress` property of the `<Button>` component in **components/Button.js**:
87
88{/* prettier-ignore */}
89```jsx Button.js
90export default function Button({ label,  theme, /* @info Pass this prop to trigger the handler method from the parent component. */ onPress/* @end */}) {
91  // ...rest of the code remains same
92  if (theme === "primary") {
93    return (
94      <View>
95        /* ...rest of the code remains same */
96        <Pressable
97          style={[styles.button, { backgroundColor: '#fff' }]}
98          /* @info */ onPress={onPress} /* @end */
99        >
100      </View>
101    );
102  }
103}
104```
105
106In **App.js**, add the `pickImageAsync()` function to the `onPress` prop on the first `<Button>`.
107
108{/* prettier-ignore */}
109```jsx App.js
110export default function App() {
111  // ...rest of the code remains same
112
113  return (
114    <View style={styles.container}>
115      /* ...rest of the code remains same */
116      <Button theme="primary" label="Choose a photo" /* @info */ onPress={pickImageAsync} /* @end */ />
117    </View>
118  );
119}
120```
121
122The `pickImageAsync()` function is responsible for invoking `ImagePicker.launchImageLibraryAsync()` and then handling the result.
123The `launchImageLibraryAsync()` method returns an object containing information about the selected image.
124
125To demonstrate what properties the `result` object contains, here is an example result object:
126
127```json
128{
129  "assets": [
130    {
131      "assetId": null,
132      "base64": null,
133      "duration": null,
134      "exif": null,
135      "height": 4800,
136      "rotation": null,
137      "type": "image",
138      "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%username%252Fsticker-smash-47-beta/ImagePicker/77c4e56f-4ccc-4c83-8634-fc376597b6fb.jpeg",
139      "width": 3200
140    }
141  ],
142  "canceled": false,
143  "cancelled": false
144}
145```
146
147</Step>
148
149<Step label="4">
150
151## Use the selected image
152
153The `result` object provides the `assets` array, which contains the `uri` of the selected image.
154Let's take this value from the image picker and use it to show the selected image in the app.
155
156Modify the **App.js** file in the following steps:
157
158- Declare a state variable called `selectedImage` using the <A href="https://react.dev/learn/state-a-components-memory#adding-a-state-variable" openInNewTab>`useState`</A> hook from React.
159  We'll use this state variable to hold the URI of the selected image.
160- Update the `pickImageAsync()` function to save the image URI in the `selectedImage` state variable.
161- Then, pass the `selectedImage` as a prop to the `ImageViewer` component.
162
163{/* prettier-ignore */}
164```jsx App.js
165/* @info Import useState hook from React. */ import { useState } from 'react'; /* @end */
166// ...rest of the import statements remain unchanged
167
168export default function App() {
169  /* @info Create a state variable that will hold the value of selected image. */
170  const [selectedImage, setSelectedImage] = useState(null);
171  /* @end */
172
173  const pickImageAsync = async () => {
174    let result = await ImagePicker.launchImageLibraryAsync({
175      allowsEditing: true,
176      quality: 1,
177    });
178
179    if (!result.canceled) {
180      /* @info Pick the first uri from the assets array. Also, there is only one image selected at a time so you don't have to change this. */
181      setSelectedImage(result.assets[0].uri);
182      /* @end */
183    } else {
184      alert('You did not select any image.');
185    }
186  };
187
188  return (
189    <View style={styles.container}>
190      <View style={styles.imageContainer}>
191        /* @info Pass the selected image URI to the ImageViewer component. */<ImageViewer
192          placeholderImageSource={PlaceholderImage}
193          selectedImage={selectedImage}
194        />
195        /* @end */
196      </View>
197      /* ...rest of the code remains same */
198    </View>
199  );
200}
201```
202
203Now, modify the **components/ImageViewer.js** file to conditionally display the selected image in place of the placeholder image.
204We'll need to pass the `selectedImage` prop to the component.
205
206The source of the image is getting long, so let's also move it to a separate variable called `imageSource`.
207Then, pass it as the value of the `source` prop on the `<Image>` component.
208
209<SnackInline
210label="Image picker"
211templateId="tutorial/02-image-picker/App"
212dependencies={['expo-image-picker', 'expo-status-bar', '@expo/vector-icons', '@expo/vector-icons/FontAwesome']}
213files={{
214'assets/images/background-image.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/503001f14bb7b8fe48a4e318ad07e910',
215'components/ImageViewer.js': 'tutorial/02-image-picker/ImageViewer.js',
216'components/Button.js': 'tutorial/02-image-picker/Button.js'
217}}>
218
219{/* prettier-ignore */}
220```jsx
221export default function ImageViewer({ placeholderImageSource, /* @info Pass the selectedImage prop.*/selectedImage/* @end */ }) {
222  /* @info If the selected image is not null, show the image from the device, otherwise, show the placeholder image. */
223  const imageSource = selectedImage  ? { uri: selectedImage } : placeholderImageSource;
224  /* @end */
225
226  return <Image /* @info */source={imageSource}/* @end */ style={styles.image} />;
227}
228```
229
230</SnackInline>
231
232In the above snippet, the `<Image>` component uses a conditional operator to load the source of the image.
233This is because the image picked from the image picker is a <A href="https://reactnative.dev/docs/images#network-images" openInNewTab>`uri` string</A>,
234not a local asset like the placeholder image.
235
236Let's take a look at our app now:
237
238<Video file="tutorial/03-image-picker-demo.mp4" />
239
240> The images used for the demo in this tutorial were picked from [Unsplash](https://unsplash.com).
241
242</Step>
243
244## Next step
245
246We added the functionality to pick an image from the device's media library.
247
248<BoxLink
249  title="Create an emoji picker modal"
250  Icon={BookOpen02Icon}
251  description="In the next chapter, we'll learn how to create an emoji picker modal component."
252  href="/tutorial/create-a-modal"
253/>
254