Go Cordova-free before Capacitor 9
Capacitor 9 makes the Cordova layer optional. Find every Cordova plugin in your app, swap each for its Capacitor equivalent with a replacement table, and retire cordova-plugin-ionic and CodePush along the way.
The headline feature of Capacitor 9 is that Cordova becomes optional. Today, every Capacitor app carries a Cordova compatibility layer so that old cordova-plugin-* packages keep working. From Capacitor 9, an app with no Cordova plugins can leave that layer out entirely. Ionic forecasts Capacitor 9 for the end of November 2026, and 9.0.0-alpha.7 landed on 18 September.
You do not have to remove Cordova plugins to upgrade. But most apps carry two or three that were added years ago and never revisited, and this is the right moment to replace them. It means a smaller app, fewer build warnings, and plugins that are actually maintained.
1. Find every Cordova plugin
The Capacitor CLI lists both kinds of plugin:
npx cap ls
Also check package.json directly, because some come in as dependencies of other packages:
npm ls --all 2>/dev/null | grep -iE "cordova-plugin|phonegap-plugin|@awesome-cordova-plugins"
@awesome-cordova-plugins/* (formerly @ionic-native) are only TypeScript wrappers. Each one points to a real Cordova plugin underneath, which is the thing to replace.
2. Replace each plugin
Most common Cordova plugins have a direct Capacitor replacement:
| Cordova plugin | Capacitor replacement |
|---|---|
| cordova-plugin-camera | @capacitor/camera |
| cordova-plugin-geolocation | @capacitor/geolocation |
| cordova-plugin-file | @capacitor/filesystem |
| cordova-plugin-device | @capacitor/device |
| cordova-plugin-network-information | @capacitor/network |
| cordova-plugin-statusbar | @capacitor/status-bar |
| cordova-plugin-splashscreen | @capacitor/splash-screen |
| cordova-plugin-inappbrowser | @capacitor/inappbrowser or @capacitor/browser |
| cordova-plugin-dialogs | @capacitor/dialog |
| cordova-plugin-vibration | @capacitor/haptics |
| cordova-plugin-x-socialsharing | @capacitor/share |
| cordova-plugin-local-notification | @capacitor/local-notifications |
| phonegap-plugin-push / cordova-plugin-firebasex (push) | @capacitor/push-notifications |
| cordova-plugin-keyboard | @capacitor/keyboard |
| cordova-plugin-screen-orientation | @capacitor/screen-orientation |
| cordova-plugin-nativestorage | @capacitor/preferences |
| phonegap-plugin-barcodescanner | @capacitor-mlkit/barcode-scanning |
| cordova-plugin-whitelist | Not needed in Capacitor; remove it |
| cordova-plugin-code-push | A Capacitor live update plugin such as OtaKit |
| cordova-plugin-ionic (Appflow Live Updates) | A Capacitor live update plugin such as OtaKit |
APIs differ slightly. Capacitor plugins return promises instead of taking success and error callbacks, and option names are not always identical. A typical change:
// Before: Cordova
navigator.camera.getPicture(onSuccess, onError, {
quality: 80,
destinationType: Camera.DestinationType.FILE_URI,
});
// After: Capacitor
import { Camera, CameraResultType } from '@capacitor/camera';
const photo = await Camera.getPhoto({
quality: 80,
resultType: CameraResultType.Uri,
});If a plugin has no replacement, look for a maintained community plugin in the Capawesome or Capacitor Community organizations, or write a small local plugin. A focused local plugin is often less code than the Cordova plugin it replaces.
3. Remove the old plugin completely
npm uninstall cordova-plugin-camera @awesome-cordova-plugins/camera npx cap sync
Then clean up what Cordova leaves behind:
- Preferences for the old plugin in
capacitor.config.tsundercordova.preferences. - Permission strings in
Info.plistandAndroidManifest.xmlthat no other plugin uses. App Review asks about permissions you declare but do not use. - Any
devicereadylisteners. Capacitor plugins are ready when your app loads.
4. Do it one plugin per release
Each replacement changes native code, so each needs a store build and real-device testing. Batching all of them into the Capacitor 9 upgrade means debugging five changes at once. A calmer order:
- Replace one or two plugins per native release over the next two months.
- Finish the Swift Package Manager migration, which Capacitor 9 and the end of CocoaPods Trunk both push you toward.
- Upgrade to Capacitor 9 when it is stable, as its own release. See what changes in Capacitor 9.
Still on Cordova's live update plugin? CodePush was retired with App Center, and Appflow is winding down. Replacing that plugin is the most urgent item on this list, because a dead update server leaves users stuck on whatever bundle they have. See migrating from App Center and Appflow alternatives.
5. Keep web and native releases apart
Plugin replacements are native changes and must go through the stores. The JavaScript that calls the new plugin, however, is web code, and it will need adjustments after real users hit edge cases: a permission prompt at the wrong moment, a changed file path, a different error shape.
With OtaKit, those follow-up fixes ship over the air. The CLI compares your web build with the native plugins in the store build before it releases, so a bundle that calls a plugin users do not have yet is stopped at upload, not discovered as a crash. Bump runtimeVersion with each native release that changes the plugin set, and OtaKit keeps old and new shells on the right bundles automatically.