Take photos from a Capacitor web app using the Camera plugin
Add camera access to a Capacitor web app using the official Camera plugin, with platform permission setup and memory trade-offs.
21 Mar 2026, 08:59 UTC

The problem: a web view cannot open the camera by itself
Your Capacitor app needs a photo upload button, but the embedded web view (the browser runtime inside the native app) has no direct access to the device camera. Writing separate Swift and Kotlin modules just to capture one image defeats the benefit of sharing a single web codebase across iOS and Android.
The official @capacitor/camera plugin bridges that gap. It exposes native camera APIs to JavaScript, so a web app can request a photo and receive a URI or base64 string back without custom native code for basic capture.
Install the plugin and sync native projects
Run these commands in the terminal at the Capacitor project root. No special permissions are required beyond normal file access.
npm install @capacitor/camera
npx cap sync
npx cap sync copies the web build into the native iOS and Android projects and registers the plugin. It also overwrites generated native app files, so do not edit code directly inside ios/App or android/app unless you are prepared to lose those changes on the next sync.
Expected check: @capacitor/camera appears in package.json, and the native projects are updated without errors.
Add platform permissions
Camera access is gated by the operating system, not by Capacitor alone. You must declare usage descriptions before the plugin can prompt the user.
- Android: add the
CAMERApermission toandroid/app/src/main/AndroidManifest.xml. - iOS: add a
NSCameraUsageDescriptionstring toios/App/Info.plist, explaining why the app needs the camera.
Permission keys and wording can vary by plugin version. Check the installed @capacitor/camera README in node_modules for the exact keys that match your Capacitor major version.
Capture a photo from JavaScript
This example requests a file URI rather than inline base64 data, which keeps memory usage lower for large images.
import { Camera, CameraResultType } from '@capacitor/camera';
async function takePhoto() {
const image = await Camera.getPhoto({
resultType: CameraResultType.Uri,
quality: 90,
});
const preview = document.getElementById('photo-preview');
if (preview && image.webPath) {
preview.src = image.webPath;
}
}
CameraResultType.Uri returns a filesystem path the web view can display. CameraResultType.Base64 returns the full image as a data URL string, which is convenient for small thumbnails but can consume significant memory on high-resolution photos.
Trade-offs and how to verify the result
- Memory: prefer
UrioverBase64for full-resolution images. - Native edits are temporary: any change made directly to synced native project files disappears on the next
npx cap sync. Make configuration changes in Capacitor config or plugin configuration instead. - Version alignment: the camera plugin major version should match your installed
@capacitor/coremajor version; mismatches can cause runtime failures. - Real devices only: simulators often lack camera hardware, so permission prompts and photo return should be confirmed on a physical iOS or Android device.
Practical check: build and run the app on a physical device, tap the capture button, confirm the operating system permission prompt appears, and verify the selected image renders in the web view preview.
Closing
Start with CameraResultType.Uri, add the required iOS and Android permission strings, and test on a real device. If you later need advanced controls such as manual focus or flash settings, evaluate whether the plugin supports them or whether a thin native module is justified.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.