5 min read Emadideen Ghannam
Flutter, Ionic, or Cordova: a 14-year mobile cross-platform retrospective
I have shipped production apps with all three. Here is what I'd pick today, what I'd avoid, and the honest reason native still wins for some products.
I have shipped production mobile apps with Cordova in 2016, Ionic in 2018, and Flutter in 2022. Same kind of product each time: enterprise apps with complex forms, offline-capable, multi-language, deployed to a few thousand users. Three different stacks. Three very different experiences.
This is the retrospective I wish I had read in 2016.
Cordova: the one I would not pick again
Cordova wraps a webview in a native app shell. You write HTML, CSS, JS. The bridge gives you access to native APIs through plugins.
In 2016 it was the only realistic cross-platform option. The team I was on shipped a stocktaking app with it. The app worked. The development experience was painful in three specific ways:
Plugins broke on every iOS update. Apple changes the camera API, the camera plugin breaks, you wait for the maintainer to fix it, you upgrade, you ship. The plugin maintainer was often a hobbyist with a day job. Critical apps depending on volunteer maintenance is a risk you cannot price.
Webview performance was inconsistent. A list with 200 items rendered fine on the latest iPhone and stuttered on a three-year-old Android. We spent weeks tuning. The tuning didn’t survive the next OS update.
Native debugging was a nightmare. When a plugin failed, you got a webview error, then a JavaScript bridge error, then a native stack trace. Three layers, none of them with a clean source map.
I would not pick Cordova in 2026. The webview model has been superseded by Capacitor (a better Cordova) and Ionic (a Cordova/Capacitor wrapper with a UI framework). Pure Cordova is a legacy choice.
Ionic: the one I’d pick if you have an Angular team
Ionic shipped a couple of years after Cordova and absorbed it. Angular team. Same web tech, but with a UI framework that adapts platform conventions (iOS spinner, Android ripple, etc.) and Capacitor as the modern bridge.
I shipped a stocktaking app and a retail app on Ionic in 2018. The experience was meaningfully better than Cordova. The same Angular code I wrote for the web ran on the phone with native-feeling components. Capacitor’s plugin model was more stable than Cordova’s. The hot reload was real.
The wins:
- One codebase, three targets. Web, iOS, Android. We shipped to all three from the same Angular project.
- The team only knew Angular. No new mental model. The mobile-specific bits (gestures, hardware) were the only new surface area.
- Capacitor plugins survived OS updates better than Cordova plugins ever did.
The losses:
- Performance was good, not great. A scrolling list of 1,000 items was 60fps on iOS, 50fps on a mid-range Android. Native equivalent is 60fps everywhere.
- The native feel was 90 percent there. A power user on iOS could tell it wasn’t native. The shadows, the ripple, the haptics. Close, not identical.
- Build time was slow. A clean Capacitor build took five minutes. With CI sharing across web and mobile, this added up.
For an internal enterprise app where users are not comparing it to Instagram, Ionic + Capacitor is a great choice in 2026. The team you already have can ship to mobile without a separate iOS/Android team.
Flutter: the one I’d pick today for greenfield
Flutter is a different model. Not a webview. Not a JavaScript bridge. A widget tree rendered by Dart with a native canvas. The result feels native because it draws everything itself, frame by frame, in 60fps consistently.
I shipped an attendance app on Flutter in 2024. The experience was the cleanest of the three by a wide margin.
The wins:
- Performance is genuinely good. 60fps lists, 60fps animations, 60fps everywhere. No tuning required.
- Hot reload is fast. Saving a file and seeing the change in under a second is the new baseline. After Flutter, the slower hot reloads in Ionic feel old.
- The widget API is consistent. Once you learn it, you’re fast. There is no “iOS does this differently from Android” surface to think about - the widgets handle it.
- Dart is a fine language. Boring, in a good way. Sound null safety. Records. Pattern matching. After 14 years of TypeScript, Dart took two weeks to feel comfortable.
The losses:
- Dart is a separate ecosystem. The team has to learn another language. Not the existing TypeScript stack. Not shared with the web codebase.
- The web build is real but second-class. Flutter web ships, but the bundle is large and the SEO story is poor. Don’t use Flutter for the web; use it for mobile.
- Native interop is tighter than Capacitor. Calling a native plugin requires writing Dart bindings to Swift/Kotlin. Capacitor’s plugin model is more accessible to a web team.
What I would actually pick in 2026
A decision tree:
- You have an Angular/React team and the product is enterprise SaaS, internal tools, or anything where users won’t compare it to TikTok: Ionic + Capacitor. Reuse the team and the codebase.
- You’re building a consumer product, performance matters, and the team has the runway to learn Dart: Flutter. The 60fps consistency is worth the language switch.
- You need deep platform-specific features (ARKit, CoreML, complex audio, native biometrics flows): native iOS + Android. Don’t even start with cross-platform. The bridges always lag.
- You’re inheriting a Cordova app: plan a migration to Capacitor. Same web code, modern bridge.
What stayed the same across all three
The hard parts of mobile are not the framework. They are:
- Offline data sync.
- Push notification reliability across iOS, Android, and the web.
- Deep linking.
- App store reviews.
- The hour you’ll spend explaining to a product manager why iOS users have to grant location permission separately for “always” vs “while in use.”
No framework solves these. Pick the one that lets your team focus on solving them.