At run time both put your web app in the system WebView and serve it from https://localhost. The differences lie in who owns the native side and how JavaScript reaches it:
| Cordova 13 | Capacitor 8 | |
|---|---|---|
| Configuration | config.xml, merged on every prepare | capacitor.config.json, plus native files |
| CLI | Global cordova | Per-project @capacitor/cli, via npx cap |
| Native projects | Generated in platforms/ | android/, ios/, committed (Cordova's platforms/) |
| Android bridge | addJavascriptInterface, a secret | addWebMessageListener, allowed origins |
| Plugin API | exec() callbacks, window globals | Promises, imported modules, types |
| Browser support | The browser platform | A web implementation per plugin |
| Updates to native code | platform rm, platform add | npm 2,036 update, cap sync, cap migrate |
Two rows deserve a comment. The bridge: Cordova's addJavascriptInterface object is visible to any page the WebView loads, which is why it needs the per-launch secret of exec() and Callbacks, while Capacitor's addWebMessageListener delivers messages only from origins it allows (Message Passing). The native projects: you edit Capacitor's android/ directly and nothing overwrites it, but config.xml preferences such as StatusBarBackgroundColor mean nothing to Capacitor, as the next subsection shows.