One Codebase, Two App Stores: Our Honest Capacitor Experience

Every founder planning a mobile app eventually hits the same fork: build it twice, natively, for iOS and Android — roughly doubling cost and coordination forever — or build it once with a cross-platform stack and accept whatever compromises come with that. The internet is unhelpful here, because almost everyone writing about it is selling something: framework vendors publish victory laps, native purists publish post-mortems, and very few people describe what it's actually like to operate a cross-platform app in production, over years, through OS updates and store reviews.

We can offer that description. We shipped a production app to both the App Store and Google Play from a single Capacitor codebase — a web app (Vue, in our case) wrapped in a native shell, with real users, push notifications, and paid subscriptions verified server-side. It's still running, we'd choose Capacitor again for this app, and there are apps we would absolutely not build with it. Here's the unsponsored version.

What Capacitor actually is (and how it differs from React Native or Flutter)

Cross-platform frameworks split into two families. React Native and Flutter render native (or natively-drawn) UI from a shared codebase — closer to native feel, but a distinct skill set and their own deep ecosystems. Capacitor takes the humbler route: your app is genuinely a web app, running in the platform's WebView (WKWebView on iOS), inside a real native project that you own. Native capabilities — camera, push, purchases, filesystem, biometrics — are reached through plugins, and when no plugin fits, you write Swift or Kotlin directly in the shell, because the Xcode and Android Studio projects are sitting right there in your repo, not hidden behind a build service.

That last property is the strategic point people miss: Capacitor is web-first, not web-only. You aren't betting your product on never needing native code; you're betting that most of your app is screens, forms, lists and flows — which the web is excellent at — with native code reserved for the edges that need it.

Where it genuinely shines

  • One team, one codebase, one bug tracker. Every feature is built once, tested once, fixed once. For a small team this isn't a 2x saving on paper — it's the difference between shipping weekly and not. Features land on iOS and Android on the same day, always, because they're the same code.
  • Your web skills transfer at full value. If your team already builds for the web — ours does, daily — you skip an entire learning curve. The framework, component library, state management and test setup you already trust come with you.
  • Three platforms, not two. The same codebase is deployable as a plain website or PWA. Marketing pages, in-browser demos, letting users try the product before the store install — free.
  • Boring in the good way. The stack under you is the web platform plus a thin bridge maintained by the Ionic team. OS updates have caused us far less breakage than native-dependency churn caused on native projects we've worked on. Store review treats a Capacitor app like any other app — both stores accepted ours without WebView-related friction, which is a common unfounded fear (what the stores actually police is quality and policy compliance, not your rendering technology).

Where it hurts — the part vendors don't write

Now the honest column, from production rather than a demo weekend.

  • The ceiling on feel is real. A WebView app can get to "nobody notices" — ours did — but it takes deliberate work: respecting safe areas, matching platform scroll physics, keeping taps instant, avoiding layouts that scream website. And there's a ceiling you can't buy past. The buttery, gesture-driven, 120Hz interaction feel of a great native app is not reachable in a WebView, full stop. If your product's differentiation is interaction feel, Capacitor is the wrong tool, and no plugin fixes that.
  • Performance cliffs live in specific places. Ordinary screens are fine on any phone from the last several years. The cliffs are long lists (virtualize them or suffer), heavy animation driven from JavaScript (keep animation in CSS/compositor land), and anything that processes serious data on the UI thread. You learn where the cliffs are and route around them — but you do have to learn.
  • The plugin edge is where the real cost hides. Core plugins are solid. Step off the paved road — a niche Bluetooth device, background execution beyond the blessed patterns, advanced media pipelines — and you're evaluating a community plugin of uncertain maintenance or writing native code yourself. Budget for this honestly: our subscription integration, for example, still required genuinely understanding both stores' billing systems end to end, because the hard part was never the client button — it was the server side, which we've written up in our guide to server-side receipt validation. Cross-platform frameworks compress UI work; they do not compress platform-integration work nearly as much as the brochure implies.
  • You still need both native toolchains. "One codebase" never meant "no Xcode." You'll manage certificates, provisioning profiles, keystores, store listings, review cycles and two release pipelines. Capacitor removed duplicate feature development; it removed none of the platform release liturgy.
  • Debugging spans three worlds. Most days you're in familiar web devtools (attached to the WebView — genuinely pleasant). The bad days are bridge days, where a bug lives between web and native and you're reading Xcode logs with one hand and Safari's inspector with the other. Rare, but real, and worth knowing about before you're in one.

An honest decision rule

After operating one and building others, here's the rule we give clients, with no framework loyalty attached:

  • Capacitor is a strong default when your app is fundamentally screens, content, forms, commerce or dashboards; your team has web strength; budget or headcount rules out two native codebases; and you want web/PWA distribution as a bonus. Most business apps — honestly, most apps, period — fit this description.
  • Go native (or Flutter/React Native with native specialists) when the product's core value is interaction feel or graphics (games, camera-centric apps, pro media tools); when you depend heavily on background processing, low-level hardware access or platform-exclusive frameworks; or when you have the budget for two platform teams and interface polish is your competitive moat. These aren't edge cases — they're a different category of product, and pretending one tool covers both categories is how bad advice gets written.
  • Whichever you pick, the backend decides more than the framework. Our app's hardest engineering — subscription entitlement, sync, push infrastructure, account handling — lives on the server and would be identical under any client stack. Teams agonize over the framework choice and then under-invest in the part that actually determines whether the product works.
Cross-platform doesn't mean half the work — it means not doing the same work twice. The platform-shaped work remains, and budgeting for it honestly is what separates a shipped app from a stalled one.

The takeaway

Our verdict after real production mileage: for the large class of apps that are business logic wearing a nice interface, Capacitor is not a compromise — it's the correct engineering choice, and the "web apps feel cheap" objection is answerable with craft. For the smaller class of apps whose soul is native interaction, it's the wrong choice, and we'll be the first to say so in a scoping call, because recommending our favorite hammer for someone else's screw helps nobody. If you're weighing this fork for your own product, you can see what we've shipped on our works page — or just bring us the app idea and we'll tell you which side of the rule it lands on, including when the answer is "hire native specialists, not us."

We solve these problems on our own products every day

Free 30-min discovery call · No hard sell · Reply within one business day

Start a project

← More from the blog