1"use strict";(self.webpackChunkionic_docs=self.webpackChunkionic_docs||[]).push([["13796"],{35332(e,o,t){t.r(o),t.d(o,{metadata:()=>n,default:()=>d,frontMatter:()=>i,contentTitle:()=>s,toc:()=>c,assets:()=>l});var n=JSON.parse('{"id":"react/your-first-app/taking-photos","title":"Taking Photos with the Camera","description":"Take Photos with Camera API for iOS, Android & Web with React | Ionic Capacitor Camera","source":"@site/versioned_docs/version-v8/react/your-first-app/2-taking-photos.mdx","sourceDirName":"react/your-first-app","slug":"/react/your-first-app/taking-photos","permalink":"/docs/v8/react/your-first-app/taking-photos","draft":false,"unlisted":false,"editUrl":"https://github.com/ionic-team/ionic-docs/edit/main/versioned_docs/version-v8/react/your-first-app/2-taking-photos.mdx","tags":[],"version":"v8","sidebarPosition":2,"frontMatter":{"title":"Taking Photos with the Camera","sidebar_label":"Taking Photos"},"sidebar":"docs","previous":{"title":"Build Your First App","permalink":"/docs/v8/react/your-first-app"},"next":{"title":"Saving Photos","permalink":"/docs/v8/react/your-first-app/saving-photos"}}'),a=t(74848),r=t(28453);let i={title:"Taking Photos with the Camera",sidebar_label:"Taking Photos"},s,l={},c=[{value:"Photo Gallery Hook",id:"photo-gallery-hook",level:2},{value:"Displaying Photos",id:"displaying-photos",level:2}];function h(e){let o={a:"a",code:"code",em:"em",h2:"h2",img:"img",p:"p",pre:"pre",...(0,r.R)(),...e.components},{Head:n}=o;return n||function(e,o){throw Error("Expected "+(o?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Head",!0),(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n,{children:[(0,a.jsx)("title",{children:"Take Photos with Camera API for iOS, Android & Web with React | Ionic Capacitor Camera"}),(0,a.jsx)("meta",{name:"description",content:"Add the ability to take photos with your device's camera using the Ionic Capacitor Camera API for mobile iOS, Android, and the web. Learn how here."})]}),"\n",(0,a.jsxs)(o.p,{children:["Now for the fun part - adding the ability to take photos with the device\u2019s camera using the Capacitor ",(0,a.jsx)(o.a,{href:"/docs/v8/native/camera",children:"Camera API"}),". We\u2019ll begin with building it for the web, then make some small tweaks to make it work on mobile (iOS and Android)."]}),"\n",(0,a.jsx)(o.h2,{id:"photo-gallery-hook",children:"Photo Gallery Hook"}),"\n",(0,a.jsxs)(o.p,{children:["We will create a ",(0,a.jsx)(o.a,{href:"https://react.dev/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component",children:"custom React hook"})," to manage the photos for the gallery."]}),"\n",(0,a.jsxs)(o.p,{children:["Create a new file at ",(0,a.jsx)(o.code,{children:"src/hooks/usePhotoGallery.ts"})," and open it up."]}),"\n",(0,a.jsxs)(o.p,{children:["Next, define a new method, ",(0,a.jsx)(o.code,{children:"usePhotoGallery()"}),", that will contain the core logic to take a device photo and save it to the filesystem. Let\u2019s start by opening the device camera."]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-ts",children:"import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';\n\nexport function usePhotoGallery() {\n const addNewToGallery = async () => {\n // Take a photo\n const capturedPhoto = await Camera.getPhoto({\n resultType: CameraResultType.Uri,\n source: CameraSource.Camera,\n quality: 100,\n });\n };\n\n return {\n addNewToGallery,\n };\n}\n"})}),"\n",(0,a.jsxs)(o.p,{children:["Notice the magic here: there's no platform-specific code (web, iOS, or Android)! The Capacitor Camera plugin abstracts that away for us, leaving just one method call - ",(0,a.jsx)(o.code,{children:"Camera.getPhoto()"})," - that will open up the device's camera and allow us to take photos."]}),"\n",(0,a.jsxs)(o.p,{children:["Next, in ",(0,a.jsx)(o.code,{children:"Tab2.tsx"}),", import the ",(0,a.jsx)(o.code,{children:"usePhotoGallery()"})," method and destructure it to call its ",(0,a.jsx)(o.code,{children:"addNewToGallery()"})," method."]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-tsx",children:'import { camera } from \'ionicons/icons\';\nimport { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonFab, IonFabButton, IonIcon } from \'@ionic/react\';\n// CHANGE: Add `usePhotoGallery` import\nimport { usePhotoGallery } from \'../hooks/usePhotoGallery\';\nimport \'./Tab2.css\';\n\nconst Tab2: React.FC = () => {\n // CHANGE: Destructure `addNewToGallery()` from `usePhotoGallery()`\n const { addNewToGallery } = usePhotoGallery();\n\n return (\n <IonPage>\n <IonHeader>\n <IonToolbar>\n <IonTitle>Photo Gallery</IonTitle>\n </IonToolbar>\n </IonHeader>\n <IonContent fullscreen>\n <IonHeader collapse="condense">\n <IonToolbar>\n <IonTitle size="large">Photo Gallery</IonTitle>\n </IonToolbar>\n </IonHeader>\n\n <IonFab vertical="bottom" horizontal="center" slot="fixed">\n {/* CHANGE: Add a click event listener to the floating action button */}\n <IonFabButton onClick={() => addNewToGallery()}>\n <IonIcon icon={camera}></IonIcon>\n </IonFabButton>\n </IonFab>\n </IonContent>\n </IonPage>\n );\n};\n\nexport default Tab2;\n'})}),"\n",(0,a.jsxs)(o.p,{children:["If it's not running already, restart the development server in your browser by running ",(0,a.jsx)(o.code,{children:"ionic serve"}),". On the Photo Gallery tab, click the Camera button. If your computer has a webcam of any sort, a modal window appears. Take a selfie!"]}),"\n",(0,a.jsx)(o.p,{children:(0,a.jsx)(o.img,{alt:"A photo gallery app displaying a webcam selfie.",src:t(93720).A+"",title:"Webcam Selfie in Photo Gallery",width:"1822",height:"1816"})}),"\n",(0,a.jsx)(o.p,{children:(0,a.jsx)(o.em,{children:"(Your selfie is probably much better than mine)"})}),"\n",(0,a.jsx)(o.p,{children:"After taking a photo, it disappears right away. We need to display it within our app and save it for future access."}),"\n",(0,a.jsx)(o.h2,{id:"displaying-photos",children:"Displaying Photos"}),"\n",(0,a.jsxs)(o.p,{children:["To define the data structure for our photo metadata, create a new interface named ",(0,a.jsx)(o.code,{children:"UserPhoto"}),". Add this interface at the very bottom of the ",(0,a.jsx)(o.code,{children:"usePhotoGallery.ts"})," file, immediately after the ",(0,a.jsx)(o.code,{children:"usePhotoGallery()"})," method definition."]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-ts",children:"export function usePhotoGallery() {\n // ...existing code...\n}\n\n// CHANGE: Add the `UserPhoto` interface\nexport interface UserPhoto {\n filepath: string;\n webviewPath?: string;\n}\n"})}),"\n",(0,a.jsxs)(o.p,{children:["Above the ",(0,a.jsx)(o.code,{children:"addNewToGallery()"})," method, define an array of ",(0,a.jsx)(o.code,{children:"UserPhoto"}),", which will contain a reference to each photo captured with the Camera. Make it a state variable using React's ",(0,a.jsx)(o.a,{href:"https://react.dev/reference/react/useState",children:"useState hook"}),"."]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-ts",children:"// CHANGE: Add import\nimport { use
1State } from 'react';\nimport { Camera, CameraResultType, CameraSource } from '@capacitor/camera';\n\nexport function usePhotoGallery() {\n // CHANGE: Add the `photos` array\n const [photos, setPhotos] = useState<UserPhoto[]>([]);\n\n // ...existing code...\n}\n"})}),"\n",(0,a.jsxs)(o.p,{children:["Over in the ",(0,a.jsx)(o.code,{children:"addNewToGallery()"})," method, add the newly captured photo to the beginning of the ",(0,a.jsx)(o.code,{children:"photos"})," array. Then, update the ",(0,a.jsx)(o.code,{children:"usePhotoGallery()"})," return statement with the ",(0,a.jsx)(o.code,{children:"photos"})," array."]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-ts",children:"export function usePhotoGallery() {\n const [photos, setPhotos] = useState<UserPhoto[]>([]);\n\n // CHANGE: Update `addNewToGallery()` method\n const addNewToGallery = async () => {\n // Take a photo\n const capturedPhoto = await Camera.getPhoto({\n resultType: CameraResultType.Uri,\n source: CameraSource.Camera,\n quality: 100,\n });\n\n // CHANGE: Create the `fileName` with current timestamp\n const fileName = Date.now() + '.jpeg';\n // CHANGE: Create `savedImageFile` matching `UserPhoto` interface\n const savedImageFile = [\n {\n filepath: fileName,\n webviewPath: capturedPhoto.webPath,\n },\n ...photos,\n ];\n\n // CHANGE: Update the `photos` array with the new photo\n setPhotos(savedImageFile);\n };\n\n return {\n addNewToGallery,\n // CHANGE: Update return statement to include `photos` array\n photos,\n };\n}\n"})}),"\n",(0,a.jsxs)(o.p,{children:[(0,a.jsx)(o.code,{children:"usePhotoGallery.ts"})," should now look like this:"]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-ts",children:"import { useState } from 'react';\nimport { Camera, CameraResultType, CameraSource } from '@capacitor/camera';\n\nexport function usePhotoGallery() {\n const [photos, setPhotos] = useState<UserPhoto[]>([]);\n\n const addNewToGallery = async () => {\n // Take a photo\n const capturedPhoto = await Camera.getPhoto({\n resultType: CameraResultType.Uri,\n source: CameraSource.Camera,\n quality: 100,\n });\n\n const fileName = Date.now() + '.jpeg';\n const savedImageFile = [\n {\n filepath: fileName,\n webviewPath: capturedPhoto.webPath,\n },\n ...photos,\n ];\n\n setPhotos(savedImageFile);\n };\n\n return {\n addNewToGallery,\n photos,\n };\n}\n\nexport interface UserPhoto {\n filepath: string;\n webviewPath?: string;\n}\n"})}),"\n",(0,a.jsxs)(o.p,{children:["Next, switch to ",(0,a.jsx)(o.code,{children:"Tab2.tsx"})," to display the images. We'll add a ",(0,a.jsx)(o.a,{href:"/docs/v8/api/grid",children:"Grid component"})," to ensure the photos display neatly as they're added to the gallery. Inside the grid, loop through each photo in the ",(0,a.jsx)(o.code,{children:"UserPhoto"}),"'s ",(0,a.jsx)(o.code,{children:"photos"})," array. For each item, add an ",(0,a.jsx)(o.a,{href:"/docs/v8/api/img",children:"Image component"})," and set its ",(0,a.jsx)(o.code,{children:"src"})," property to the photo's path."]}),"\n",(0,a.jsx)(o.pre,{children:(0,a.jsx)(o.code,{className:"language-tsx",children:'import { camera } from \'ionicons/icons\';\n// CHANGE: Update import\nimport {\n IonContent,\n IonHeader,\n IonPage,\n IonTitle,\n IonToolbar,\n IonFab,\n IonFabButton,\n IonIcon,\n IonGrid,\n IonRow,\n IonCol,\n IonImg,\n} from \'@ionic/react\';\nimport { usePhotoGallery } from \'../hooks/usePhotoGallery\';\n\nconst Tab2: React.FC = () => {\n // CHANGE: Add `photos` array to destructure from `usePhotoGallery()`\n const { photos, addNewToGallery } = usePhotoGallery();\n\n return (\n <IonPage>\n <IonHeader>\n <IonToolbar>\n <IonTitle>Photo Gallery</IonTitle>\n </IonToolbar>\n </IonHeader>\n <IonContent fullscreen>\n <IonHeader collapse="condense">\n <IonToolbar>\n <IonTitle size="large">Photo Gallery</IonTitle>\n </IonToolbar>\n </IonHeader>\n\n {/* CHANGE: Add a grid component to display the photos */}\n <IonGrid>\n <IonRow>\n {/* CHANGE: Create a new column and image component for each photo */}\n {photos.map((photo) => (\n <IonCol size="6" key={photo.filepath}>\n <IonImg src={photo.webviewPath} />\n </IonCol>\n ))}\n </IonRow>\n </IonGrid>\n\n <IonFab vertical="bottom" horizontal="center" slot="fixed">\n <IonFabButton onClick={() => addNewToGallery()}>\n <IonIcon icon={camera}></IonIcon>\n </IonFabButton>\n </IonFab>\n </IonContent>\n </IonPage>\n );\n};\n\nexport default Tab2;\n'})}),"\n",(0,a.jsx)(o.p,{children:"Within the web browser, click the camera button and take another photo. This time, the photo is displayed in the Photo Gallery!"}),"\n",(0,a.jsx)(o.p,{children:"Up next, we\u2019ll add support for saving the photos to the filesystem, so they can be retrieved and displayed in our app at a later time."})]})}function d(e={}){let{wrapper:o}={...(0,r.R)(),...e.components};return o?(0,a.jsx)(o,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},93720(e,o,t){t.d(o,{A:()=>n});let n=t.p+"assets/images/camera-web-a07837d32fc6a75dd68d0d86345901c9.png"},28453(e,o,t){t.d(o,{R:()=>i,x:()=>s});var n=t(96540);let a={},r=n.createContext(a);function i(e){let o=n.useContext(r);return n.useMemo(function(){return"function"==typeof e?e(o):{...o,...e}},[o,e])}function s(e){let o;return o=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),n.createElement(r.Provider,{value:o},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.