Capacitor vs Cordova for Ionic 6+: Architecture Decision Guide
Architecture note on choosing Capacitor over Cordova for Ionic 6+ native access: requirements, minimal design, trust boundaries, failure modes, and when to change the design.
30 Apr 2026, 06:19 UTC

The Problem: Native Access Without Fragmenting the Codebase
Teams building Ionic 6+ applications need camera, geolocation, secure storage, and other device APIs across iOS, Android, and PWA targets from a single web codebase. Cordova was the historical bridge, but its plugin architecture and separate native project maintenance create friction. Capacitor is the default and recommended runtime for Ionic 6+, offering a thinner bridge, first-party plugin maintenance, and a workflow that keeps native projects out of daily development until build time.
Requirements Driving the Decision
- Single codebase for web, iOS, and Android with framework-agnostic plugins (Angular, React, Vue).
- Supported platform minimums: iOS 13+, Android 5+ (Capacitor 5+ drops iOS 12 and Android 4.4).
- Plugin availability for required native features — core plugins (Camera, Geolocation, Filesystem, Secure Storage) are maintained by the Ionic team; community plugins fill gaps but vary in maintenance.
- PWA parity where possible: web fallbacks exist for many core plugins, but not all (e.g., Bluetooth LE, background geolocation).
Smallest Suitable Design
Add only what you need. Start from an Ionic 6+ starter:
# In your Ionic project root
npm install @capacitor/core @capacitor/cli
npx cap init 'MyApp' 'com.example.myapp' --web-dir=www
npm install @capacitor/camera @capacitor/geolocation @capacitor/preferences
# Add platforms only when ready to build
npx cap add ios
npx cap add android
Where to run: project root, Node 18+ with npm. Permissions: write access to project directory; Xcode or Android Studio only needed for cap add and subsequent builds. Placeholders: app name, bundle ID, web output directory (default www for Ionic). Expected check: capacitor.config.json created with correct webDir; node_modules/@capacitor contains only installed plugins. Risk: installing plugins before cap init leaves them unregistered — run npx cap sync after any plugin add, remove, or update.
Use plugins directly in framework code — no separate native project until build:
import { Camera, CameraResultType } from '@capacitor/camera';
async function takePhoto() {
const image = await Camera.getPhoto({
quality: 90,
allowEditing: false,
resultType: CameraResultType.DataUrl
});
return image.dataUrl;
}
This runs in the web view; the bridge serializes the call to native iOS/Android implementations when on device, and uses a web fallback (input[type=file]) in PWA.
Trust and Data Boundaries
Capacitor plugins execute in the native layer but expose a JavaScript bridge. The web view never directly accesses native secrets. Cross-boundary calls are serialized JSON over the bridge (WKWebView message handlers on iOS, JavascriptInterface on Android).
- Secure storage: Use
@capacitor/preferencesfor key-value (backs to UserDefaults/SharedPreferences) or@capacitor-community/secure-storagefor platform keystore/keychain (iOS Keychain, Android Keystore). The latter enforces hardware-backed storage where available. - Biometric auth:
@capacitor/biometric-authperforms the prompt in native UI; the web layer only receives a success/failure token — no credential material crosses the bridge. - Data in transit: Bridge messages are in-process; no network exposure. However, any plugin that writes to shared storage (e.g., Filesystem plugin writing to app sandbox) must respect platform sandboxing — other apps cannot read it without explicit sharing intents.
Operational Checks
- Plugin compatibility matrix: Verify each plugin supports your target OS versions at https://capacitorjs.com/docs/plugins. Core plugins track platform deprecations; community plugins may lag.
- Environment validation: Run
npx cap doctorafter installing Xcode (macOS) or Android Studio. It checks SDK versions, CocoaPods/Gradle, and signing identities. Resolve warnings before CI/CD integration. - Real-device testing: Simulators miss permission flows, background execution limits, and plugin lifecycle (pause/resume). Test on physical devices for each platform.
- Update cadence monitoring: Core plugins (
@capacitor/*) release with Capacitor. Community plugins — check GitHub commit frequency, open issues, and release tags before adopting for production-critical features.
Failure Modes
| Failure Mode | Symptom | Root Cause | Mitigation |
|---|---|---|---|
| JS/native version mismatch | Runtime crash: 'Plugin not installed' or undefined method | Plugin updated in npm but npx cap sync not run; native project stale | Enforce cap sync in CI after npm install; lock plugin versions in package.json |
| Missing permission declarations | Silent failure — callback never fires, no error | Info.plist (iOS) or AndroidManifest.xml (Android) lacks usage description | Capacitor 5+ auto-adds some permissions; verify capacitor.config.json ios.permissions / android.permissions and rebuild native project |
| No PWA fallback | Feature works on device, throws in browser | Plugin lacks web implementation (e.g., Bluetooth LE, NFC) | Guard with Capacitor.isNativePlatform(); provide graceful degradation or alternative flow |
| Bridge latency | High-frequency sensor data arrives late or batched | Serialization overhead per bridge call | Use plugins that batch or stream (e.g., @capacitor/motion with configurable interval); avoid per-sample bridge calls |
Conditions That Change the Design
- Missing plugin: If a required native feature has no Capacitor plugin, you have three paths — wrap a Cordova plugin via
@capacitor-community/cordova-plugin-wrapper, write a custom Capacitor plugin (Swift/Kotlin + TypeScript definitions), or evaluate a native module approach. The wrapper adds Cordova dependency and maintenance burden; custom plugins require native expertise. - Legacy OS support: If your device fleet includes iOS <13 or Android <5, Capacitor 5+ is incompatible. Options: stay on Capacitor 4.x (no longer receiving updates), use Cordova, or drop legacy support.
- Existing Cordova investment: Teams with custom Cordova plugins not yet migrated face migration cost. Incremental migration is possible — Capacitor can consume Cordova plugins via the wrapper — but long-term strategy should favor native Capacitor plugins.
- App Store policy shifts: Apple/Google deprecate APIs (e.g., UIWebView, Android foreground service types). Capacitor plugins generally track these, but lag can occur. Monitor release notes at https://github.com/ionic-team/capacitor/releases.
Verification Checklist
- Create a minimal Ionic 6+ starter, add
@capacitor/camera, build for iOS and Android (npx cap run ios --target='iPhone 15',npx cap run android), and verify photo capture works on device and PWA fallback (desktop browser). - Run
npx cap doctor— confirm zero configuration warnings for target platforms. - Cross-reference each required native feature against the plugin compatibility page; document any gaps.
- Review Capacitor release notes for breaking changes since your locked version; plan upgrade window.
Limitations
This note assumes Ionic 6+ with Capacitor 5+. Capacitor 6 may shift minimums or plugin APIs. Community plugin maintenance is unpredictable — audit before production use. Live reload (ionic cap run -l) does not reflect native plugin changes; rebuild the native project after any plugin install/update. App Store/Play Store review may reject apps using deprecated APIs; Capacitor plugins track platform deprecations but lag can occur.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.