Cropsly
Editorial illustration representing Native app, cross-platform, or PWA: what should you build?
← Back to BlogEngineering

Native app, cross-platform, or PWA: what should you build?

Shivam Kapoor · July 6, 2026 · 8 min read

A client came to us last year with a hotel voice assistant prototype built as a PWA. It worked fine in Chrome on a developer's laptop. Then they tested it on a mid-range Android phone in a room with 40dB of background noise. The Web Audio API added 180ms of buffering latency before their speech recognition even started, the service worker kept getting evicted under memory pressure, and the screen locked mid-conversation. That prototype took six weeks to rebuild as a native app.

The native app vs cross platform vs PWA decision isn't about which technology is "better." It's about which failure modes you can tolerate, which device capabilities you actually need, and how much platform-specific work you're willing to take on. We've shipped all three approaches across different projects, and the wrong choice always shows up as a performance problem or a missing capability, not a philosophical one.

Key Takeaways

  • PWAs are best for content-first experiences where the browser is already the right runtime. If your app needs deep OS integration, background processing, or low-latency audio, the browser will fight you.
  • Cross-platform frameworks (Flutter, React Native) win when you need one team shipping to iOS and Android with 80% shared code. You pay for it in platform-specific debugging and plugin maintenance.
  • Native is the only choice when you need hardware-level control: on-device ML inference, real-time audio processing, or background services that can't be killed.
  • The decision should be driven by your hardest requirement, not your average one. If one feature demands native, the whole app probably should be native.

What the browser gives you and what it doesn't

PWAs look attractive because deployment is trivial. You ship a URL. Updates happen server-side. No app store review, no version fragmentation, no 30% cut. For a lot of use cases that's genuinely the right trade.

But the browser is a sandbox, and sandboxes have walls. On iOS, Safari's implementation of service workers is notoriously aggressive about eviction. Background sync is limited. Push notifications work but with restrictions that change between iOS versions. Web Bluetooth and WebUSB exist but aren't supported on Safari at all. If your app needs to talk to a custom hardware peripheral, the web stack simply can't do it on iPhone.

We ran into this with RunHotel, our on-device voice AI for hotels. The initial research included a PWA prototype. It died quickly because we needed continuous audio capture at low latency, a local inference engine running Qwen3-8B on the device, and the ability to keep processing when the screen dimmed. None of those are things the browser does well. Safari throttles setInterval in background tabs, Web Audio adds mandatory buffering, and WebGPU for on-device inference is still experimental on most mobile browsers.

The practical question isn't "can the browser do this?" but "can the browser do this reliably on the worst device your users will carry?"

When cross-platform actually saves you money

Flutter and React Native both let you write one codebase and ship to iOS and Android. The pitch is compelling: one team, one language, one set of business logic, two platforms. For most CRUD apps, dashboards, and content experiences, it works.

The savings are real but narrower than the marketing suggests. You still need platform-specific UI work because iOS and Android users expect different navigation patterns, different gesture behaviors, and different haptic feedback. You still need to test on both platforms. You still need to handle platform-specific permissions, push notification setup, and deep linking.

Where cross-platform breaks down is when you need a capability that the framework's bridge doesn't expose well. React Native's bridge (or JSI in newer versions) adds overhead for high-frequency data. Flutter's custom rendering engine means you're not getting native platform widgets, which matters for accessibility and for features like iOS's native text selection handles.

We've seen teams pick React Native, build 90% of their app, then hit a wall on the last 10% that needs a native module. Now they're maintaining TypeScript and Swift and Kotlin. The cross-platform bet only pays off if your hardest feature is something the framework already handles well.

If you're building a voice AI application that needs real-time audio streaming and on-device inference, cross-platform is a risky bet. The audio stack differences between iOS and Android are significant enough that you'll end up writing platform-specific audio code anyway. At that point, you might as well own the whole stack.

The case for going native

Native development gets dismissed as expensive and slow. It's neither if you scope correctly. What it is: honest. You're working with the actual platform APIs, the actual hardware, the actual lifecycle guarantees. No abstraction layer is going to surprise you.

For on-device AI work, native is the only serious option. Running a quantized model with Metal Performance Shaders on iOS or NNAPI on Android gives you hardware acceleration that no cross-platform framework exposes cleanly. Memory management matters when you're loading a 4GB model file. Background execution limits are platform-specific and you need to handle them in platform-specific code.

abstract illustration of a smartphone split into layers showing hardware circuits beneath a clean app interface

Native also wins on cold start. A Flutter app loads the Flutter engine. A React Native app loads the JS bundle. A native app loads its own binary. On a cold start, that difference is 200 to 500ms, which is the difference between "feels instant" and "feels like an app."

The cost of native is team size. If you need iOS and Android, you need two teams or one team that's genuinely fluent in both. That's a real constraint for early-stage companies. But it's a constraint that shows up in cross-platform too, just later and at a worse moment.

How to decide without overthinking it

Start with your hardest requirement, not your feature list. Write down the single thing your app must do that would make users uninstall if it didn't work. Then check whether the browser can do it, whether a cross-platform framework can do it, or whether you need native APIs.

If your hardest requirement is offline form submission with sync, a PWA is fine. If it's rendering a complex data visualization with smooth 60fps scrolling, Flutter handles that well. If it's running a local language model and streaming audio to it in real time, go native.

Don't let the long tail of features drive this decision. Teams often pick cross-platform because 15 of their 20 features are simple list views. Then feature 16 needs camera access with custom frame processing, and they're stuck writing a native module they didn't budget for.

What about cost?

A common question we get in AI consulting engagements is whether PWA saves enough money to justify the capability tradeoffs. The answer depends on your distribution model. If you're building an internal tool for a hotel chain's staff and they all use managed Chrome devices, a PWA is perfect. No app store, no MDM deployment, just a URL.

If you're building a consumer app that needs to be discoverable in the App Store, the calculus changes. App store presence is a distribution channel, not just a deployment target. PWAs can't be listed in the App Store without wrapping them in something like Capacitor, at which point you're maintaining a hybrid stack with the downsides of both approaches.

We built a quick model for this using our AI cost estimator. The development cost difference between native and cross-platform for a moderately complex app is about 30 to 40% more for native, assuming two platforms. But the maintenance cost over two years often narrows that gap because cross-platform plugins break on OS updates, and debugging bridge issues takes longer than debugging native code.

The hybrid trap

Some teams try to split the difference: a native shell with a web view for most content, native code for performance-critical paths. This works in theory. In practice, the boundary between web and native becomes a permanent source of bugs. State synchronization, navigation consistency, and gesture conflicts all get harder.

Capacitor and similar tools have improved this pattern significantly. If you're starting from an existing web app and need to add a few native capabilities, wrapping is reasonable. If you're starting fresh and planning to wrap from day one, you're choosing the worst of both worlds: web performance constraints with native complexity.

Making the call

For most teams we talk to, the decision comes down to one question: does your app need to do something the browser fundamentally can't do? If yes, native. If no, ask whether you need app store distribution. If yes, cross-platform. If no, PWA.

The teams that get burned are the ones who pick based on what's easiest to start with rather than what's hardest to finish. A PWA prototype that works in week one can become a six-month migration in month four. A cross-platform app that handles 90% of features can spend months debugging the last 10%.

If you're weighing this decision for a specific project, reach out. We've made this call enough times that we can usually tell you which way to go in a 30-minute conversation, based on your actual requirements rather than a framework comparison matrix.

Sources

ShareTwitterLinkedIn
aiengineering

Need a team that ships?

Full-stack web, APIs, cloud, and QA. 200+ projects delivered since 2019.

Get Weekly AI Insights

Join founders and CTOs getting our AI engineering newsletter.

By subscribing, you agree to our Privacy Policy. Unsubscribe anytime.