SuperAPI AI Directory

cordova-to-capacitor

Coding & development

View repository

Updated 10-15 14:30

About this skill

Cordova to Capacitor Migration

Move a Cordova app onto Capacitor incrementally: Capacitor first, plugins one by one, Cordova removed last.

When to Use

TRIGGER when:

  • config.xml, platforms/, plugins/, cordova-android/cordova-ios, or cordova-plugin-* are present and the user wants Capacitor.
  • Code waits on deviceready or calls navigator.camera, window.plugins.*, cordova.plugins.*, @awesome-cordova-plugins/*, @ionic-native/*.
  • After migration: data loss on first launch, missing Cordova plugin behaviour, or Capacitor 9 build errors referencing Cordova symbols.

Do not use for:

  • Rewriting a Cordova plugin's Java/Objective-C as a Capacitor plugin (plugin-level port) -> follow Capacitor plugin docs; capacitor-plugins for existing replacements.
  • Upgrading Capacitor majors -> capacitor-app-upgrades.
  • Replacing Appflow Live Updates (cordova-plugin-ionic) -> ionic-appflow-migration + capgo-live-updates.
  • Framework build config (Angular output path, Vite) -> framework-to-capacitor.

Live Project Snapshot

Current migration-related packages: !node -e "const fs=require('fs');if(!fs.existsSync('package.json'))process.exit(0);const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));const out=[];for(const section of ['dependencies','devDependencies']){for(const [name,version] of Object.entries(pkg[section]||{})){if(name.includes('cordova')||name.includes('phonegap')||name.startsWith('@capacitor/')||name.startsWith('@capgo/')||name.startsWith('@ionic-native/')||name.startsWith('@awesome-cordova-plugins/')||name.startsWith('@ionic-enterprise/'))out.push(section+'.'+name+'='+version)}}if(pkg.cordova)out.push('cordova.plugins='+Object.keys(pkg.cordova.plugins||{}).join(','));console.log(out.sort().join('\n'))"

Relevant config and platform paths: !find . -maxdepth 3 -not -path '*/node_modules/*' \( -name 'config.xml' -o -name 'capacitor.config.json' -o -name 'capacitor.config.ts' -o -name 'capacitor.config.js' -o -path './ios' -o -path './android' -o -path './platforms' -o -path './plugins' -o -name 'www' \)

Key Differences That Drive Decisions

  • Capacitor native projects (ios/, android/) are source code you commit and edit; npx cap sync never regenerates them. Cordova platforms/ is a build artifact.
  • Capacitor does not apply config.xml or plugin <config-file> / <edit-config> edits at build time beyond what cap sync does for Cordova plugins. Permissions, entitlements, and Info.plist keys become manual, one-time edits.
  • Cordova plugins still work in Capacitor (installed via npm, applied on cap sync). Hooks and <variable> install prompts do not run; set variables via cordova.preferences in capacitor.config.*.
  • Origin changes: the app is served from capacitor://localhost (iOS) and https://localhost (Android) by default. Existing localStorage, IndexedDB, cookies, and service-worker data are tied to the old origin and look "lost" after the update unless you keep the scheme (see references/config-and-data.md).
  • deviceready is not needed. Plugins are available after import; for remaining Cordova plugins, deviceready still fires because Capacitor's Cordova layer injects cordova.js.

Capacitor 9: Cordova runtime is optional

On Capacitor 9, npx cap sync only wires in the Cordova compatibility layer when at least one installed plugin is a Cordova plugin:

  • Android: capacitor-cordova-android / capacitor-cordova-android-plugins modules disappear from settings.gradle and app/build.gradle when there are none.
  • iOS: CapacitorCordova is no longer added to the Podfile or Package.swift.

Implications:

  • Removing the last Cordova plugin shrinks the app and drops cordova.js - good, but any app/native code that references Cordova symbols (com.getcapacitor.cordova.CordovaPlugin, CDVPlugin, CDVPluginHandleOpenURL, window.cordova) without a Cordova plugin installed fails to compile or is undefined at runtime.
  • There is no config flag to force-include the runtime. Either remove those references or keep a Cordova plugin installed.
  • deviceready listeners left in JS never fire once the runtime is gone. Remove them before removing the last Cordova plugin.
  • On Capacitor 8 the runtime is always bundled, so this only surfaces on upgrade; check before moving to 9.

Procedure

  1. Audit (report to the user before editing):
    • cordova --version, cordova platform ls, cordova plugin ls (or package.json cordova.plugins).
    • config.xml: widget id, version, <preference>s, <platform> blocks, <access>/<allow-navigation>, icons/splash.
    • Which webview plugin is installed (cordova-plugin-ionic-webview, cordova-plugin-wkwebview-engine) and the current scheme/hostname - decides data preservation.
    • Every plugin: replacement exists, keep as Cordova plugin, or blocker. Use references/plugin-mapping.md.
    • Code using deviceready, navigator.* Cordova globals, window.plugins, Ionic Native wrappers.
  2. Install Capacitor in the existing project and keep the same app ID:
    npm install @capacitor/core @capacitor/ios @capacitor/android
    npm install -D @capacitor/cli
    npx cap init "<name from config.xml>" <widget id from config.xml> --web-dir www
    
    cap init reads config.xml preferences into cordova.preferences in the Capacitor config. Make sure the web build outputs to the webDir (Ionic Angular: www).
  3. Decide data preservation (scheme) before the first release: references/config-and-data.md.
  4. npm run build && npx cap add ios && npx cap add android. Cordova plugins listed in package.json dependencies are installed into the new native projects; known-incompatible ones are skipped with a warning.
  5. Port native configuration by hand: usage strings, permissions, entitlements, URL schemes, orientation, splash/icons (npx @capacitor/assets generate). See references/config-and-data.md.
  6. Replace plugins one at a time (references/plugin-mapping.md), convert callback code to promises (references/code-patterns.md), npx cap sync, test on device after each.
  7. Remove Cordova when nothing depends on it (see Removal). Keep Cordova plugins that have no replacement; they work.
  8. Offer Capgo live updates once the Capacitor build is stable (capgo-live-updates).

Removal

Only after every flow is verified on both platforms:

npm uninstall cordova cordova-ios cordova-android
npm uninstall cordova-plugin-ionic-webview cordova-plugin-splashscreen cordova-plugin-statusbar cordova-plugin-ionic-keyboard
rm -rf platforms plugins
git mv config.xml config.xml.bak   # keep for reference until release
npx cap sync
grep -rn "deviceready\|cordova\.\|window\.plugins" src --include=*.ts --include=*.js --include=*.tsx --include=*.vue

Remove the cordova block from package.json if no Cordova plugins remain. Do not run cordova plugin rm loops; they edit platforms/ you are deleting anyway.

Verification

  • npx cap sync output lists only intended Cordova plugins and no "incompatible" surprises.
  • npx cap doctor shows matching versions for core, cli, ios, android.
  • Install the Capacitor build over the existing store version (same bundle ID, higher build number) on a device and confirm the user is still logged in and local data is present.
  • Exercise every migrated plugin on real devices (camera, files, push token, geolocation, purchases).
  • Capacitor 9: after removing the last Cordova plugin, grep -rn "CapacitorCordova\|capacitor-cordova-android" ios android returns nothing and the native build succeeds.

Error Handling

Error / symptom Fix
Found 1 incompatible Cordova plugin for ios, skipped install: during sync Plugin is on Capacitor's known-incompatible list; use the Capacitor equivalent
User data / login gone after updating from the Cordova build Origin changed; set server.iosScheme: 'ionic' (old ionic-webview iOS) or match the old Android scheme/hostname, then ship again
deviceready handler never runs Capacitor 9 with no Cordova plugins, or code expecting Cordova globals; call plugins directly
"Camera" plugin is not implemented on ios (any plugin name) Plugin not installed in native project; npx cap sync, check npx cap ls
Cordova plugin variable missing (API key etc.) at build Add it under cordova.preferences in capacitor.config.*, then npx cap sync
Android: cannot find symbol class CordovaPlugin / iOS: No such module 'Cordova' after Capacitor 9 upgrade Cordova runtime no longer bundled; remove the reference or keep a Cordova plugin
Plugin with install hooks does nothing Hooks do not run in Capacitor; replicate the hook's file edits manually
White screen after cap add webDir wrong or web build not run; npm run build then npx cap sync

References

  • references/plugin-mapping.md - Cordova plugin -> Capacitor/Capgo replacement table, incompatible list, keep-as-Cordova guidance.
  • references/config-and-data.md - config.xml mapping, preferences, schemes and data preservation, permissions, splash/icons.
  • references/code-patterns.md - callback-to-promise conversions for common plugins.

Related skills: capacitor-plugins, capacitor-app-upgrades, capgo-live-updates, capacitor-app-store.