PageSourceSearch

https://ionicframework.com/docs/assets/js/96054040.5dd79286.js

js ionicframework.com collected 2026-09-24 15:16:03 UTC 13,215 bytes, 1 lines download raw bytes

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.