Using Capacitor's Camera Plugin to Access Native Camera from a Web Codebase
Learn how to add Capacitor's Camera plugin to a hybrid app, capture a photo with a few lines of TypeScript, and verify the native setup on both iOS and Android.
31 Dec 2025, 01:40 UTC

Problem: you need a photo from the device camera without writing separate iOS/Android code
Many hybrid apps start with a single web codebase (HTML, CSS, JavaScript/TypeScript) but still require access to the device's camera. Writing platform-specific Swift or Kotlin for each release adds maintenance overhead and risks divergence between the web UI and native features.
Thesis: Capacitor's @capacitor/camera plugin gives a unified, promise-based API that bridges your web layer to the native camera, and you can verify the integration with a few concrete steps
How the plugin works
Capacitor provides a thin native bridge for each platform. When you install @capacitor/camera, Capacitor wires the plugin's Swift (iOS) and Kotlin (Android) implementations into your native projects. Calling Camera.getPhoto() from JavaScript returns a Promise that resolves to either a base64 string or a file URI, depending on the options you pass. The plugin also triggers the system permission dialog automatically.
Setup steps
- Create a Capacitor project (if you don't already have one). Run these commands in a terminal with Node.js and npm installed; no special privileges are needed beyond write access to the project folder:
npm init @ionic/app my-camera-app --type=angular cd my-camera-app npx cap add ios npx cap add android - Install the Camera plugin:
npm install @capacitor/camera npx cap syncThe
cap syncstep copies the plugin's native code into the iOS Xcode project and Android Studio module and updates the configuration files. - Add required usage descriptions (a manual step; omitting them causes a runtime crash when the permission dialog appears):
- iOS: open
ios/App/App/Info.plistand add the keyNSCameraUsageDescriptionwith a string explaining why the app needs the camera (for example, "To capture profile pictures"). - Android: open
android/app/src/main/AndroidManifest.xmland ensure the line<uses-permission android:name="android.permission.CAMERA" />is present.
After editing, run
npx cap syncagain so the changes are picked up. - iOS: open
Worked example: capturing a photo and displaying it
In any Angular, React, or Vue component (or a plain TypeScript file) you can use the plugin like this:
import { Camera, CameraResultType, CameraSource, CameraDirection } from '@capacitor/camera';
async function takePicture(): Promise<string | null> {
try {
const photo = await Camera.getPhoto({
quality: 90,
resultType: CameraResultType.Uri, // returns a file URI
source: CameraSource.Camera, // use the camera, not the photo library
direction: CameraDirection.Rear,
});
// photo.webPath is a safe URL you can bind to an <img> tag
return photo.webPath ?? null;
} catch (err) {
console.error('Unable to take picture', err);
return null;
}
}Bind the returned webPath to an image element:
<img [src]="pictureUrl" alt="Captured photo" />To try it:
- Run the app on a physical device or emulator:
npx cap run iosornpx cap run android. - When prompted, grant camera permission.
- Tap the button that calls
takePicture(); the captured photo should appear in the<img>element.
If the image does not appear, open the native project and verify:
- iOS:
Info.plistcontainsNSCameraUsageDescription. - Android:
AndroidManifest.xmlincludes<uses-permission android:name="android.permission.CAMERA" />.
Trade-offs and limitations
Memory usage: Choosing resultType: CameraResultType.Base64 returns the image as a base64 string. For photos larger than a few hundred kilobytes this can consume significant RAM and slow the web view down. Using a file URI (CameraResultType.Uri) or saving the photo via the Filesystem plugin is preferable for larger images.
Feature lag: The official plugin tracks a stable set of camera capabilities (auto-focus, flash, resolution). Cutting-edge APIs such as manual focus, RAW capture, or exposure bracketing are not exposed. If you need those, you must write a custom Capacitor plugin or fork the existing one.
Web-view CSP: On iOS, Capacitor uses WKWebView, which enforces a strict Content Security Policy. Inline script blocks or inline styles may be blocked unless you add appropriate meta tags or server headers. Keeping JavaScript in external files avoids most of these issues.
Actionable closing
Start by adding the plugin to an existing Capacitor project, remember to manually insert the usage descriptions, and prefer returning a file URI for photos larger than a thumbnail. Test on a real device, because emulators may not provide a functional camera feed. With these steps you can keep a single web codebase while still accessing the native camera reliably across iOS and Android.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.