I Didn’t Want “Built with Web Tech” to Mean Compromise [Capacitor/Cordova]
![I Didn’t Want “Built with Web Tech” to Mean Compromise [Capacitor/Cordova]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsg61yhm85pcev1kus9c.gif)
HTML, CSS, JavaScript. The same technologies you use for websites and web apps can also power iOS and Android apps. Capacitor and Cordova let you run those web interfaces inside a mobile app, with plugins providing access to features such as push notifications and Face ID.
The component that displays the web interface inside the app is called a WebView. Apps built this way are often called WebView or hybrid apps. They aren't just small experiments: in Japan, examples include AEON's iAEON shopping app and the Sumitomo Mitsui Banking Corporation app.
There are challenges, though. Recreating native UI with web technologies has limits. Liquid Glass, introduced in iOS 26, is a good example: the glass changes shape under your finger and refracts the content behind it.
Then there are ads. Google AdMob is a common choice for mobile apps, but making a native ad scroll between posts in a web feed is a different problem from placing a banner on the screen. The web content and the native UI live in separate rendering layers.
For almost ten years, I thought of these as limitations of WebView apps. But after questioning the requirements and experimenting, I finally found a way to make native UI and web content coexist without disrupting the experience!
Native UI alongside web content
Close this web-based sheet, and the iOS tab bar appears underneath. You can then interact with it, including a long press. The tab bar is native UI rendered by UIKit.
Web content and native UI, behaving naturally on the same screen. I honestly used to think this wasn't possible!
An iOS tab bar that controls the web app
Compare a long press on a web recreation with the native tab bar. The curvature of the glass and the distortion of the photo behind it are different.
I built @rdlabo/capacitor-native-ui to use this native UI alongside web content. It reads supported Ionic components and renders their native counterparts with UIKit and SwiftUI.
Labels and selection state can still be managed on the web side. Ionic isn't required, either: in a plain HTML app, you can explicitly mark buttons and back buttons for native rendering.
I've also released a Cordova plugin. It shares the native rendering implementation, so Cordova apps can use it too.
Native ads that scroll with the posts
Navigation wasn't the only place I wanted native UI. I wanted ads between posts, scrolling with the feed, as they do on X. The posts and photos are web content; the ads between them are rendered by the Google Mobile Ads SDK.
Both platforms are showing test ads.
After tuning the behavior separately on iOS and Android, I made this available through @capacitor-community/admob.
When you can't interact with it, could an image do?
The hardest part of both features was the boundary between the web and native rendering layers.
CSS z-index only controls stacking inside the WebView. A web sheet cannot cover a native view placed above the WebView itself. Scrolling has the same problem: moving the web content doesn't automatically move the native view with it.
They are separate rendering systems. Of course they behave differently. But to the person using the app, it's all one screen.
So I reconsidered the requirement: when does the UI actually need to remain a live native view?
The native UI itself doesn't need to enter the DOM. Only its appearance does. That realization was a thrill. I call the approach “Projection.”
But taking a snapshot isn't the whole solution. Hide the native view first, and you get a blank frame before its image appears. Show the image first, and the translucent glass appears twice, layered over itself.
Neither works.
So I prepare the snapshot and get it ready for display on the web side before switching. When hiding the native view, I temporarily display the same image on the native side to bridge the handoff. Once the web side displays its copy, I remove the temporary native-side image in step with rendering. The appearance stays the same; only the place rendering it changes.
Tuning that handoff frame by frame produced the behavior in the opening demo. Just watching it, you can't tell when the tab bar becomes an image.
It feels like magic!
There are limits: a snapshot cannot preserve interaction or video playback. Scrolling also calls for different strategies. In supported iOS scroll regions, the live native view follows the content. For Android ads that can be captured, an image is projected while scrolling.
I extracted the shared coordination into WebView Native UI Engine, so other plugin authors can use it. My Projection article goes deeper into preparing the snapshot and timing the handoff.
Adapting the app to iPhone Duo
Using native UI also gave me a way to support iPhone Duo!
On Duo's outer display and its inner display in landscape, back buttons and tab bars move to the sides. This arrangement is called Vertical Bars. Moving navigation to the sides gives the same Ionic app more vertical room for its content.
This recording enables the vertical layout using a switch inside the demo app.
Moving the bars is only part of the work. The web content needs to leave room for them, too. The background can fill the screen, while cards and lists stay clear of the bars. Safe areas become a question of how the content and controls fit together, not just how much padding to put at the top and bottom.
Vertical Bars follow iOS conventions for width, button order, grouping, and animation. Other apps follow those same conventions, which makes small differences in a web recreation easier to notice. Matching the transitions and the changing shape of the glass is particularly tricky. This is where I want to use native UI.
A plain HTML app can use it too. In this example, pressing the native back button on the right runs the existing web-side handler and returns to the list.
Outside Ionic, this integration supports explicitly marked buttons and back buttons. It does not automatically convert arbitrary HTML tab bars.
Device layout information comes from Erkam Yaman's Capacitor Foldable. I use that information to align the web layout with the native controls. Thank you, Erkam!
You still need to initialize the plugins and connect the layout updates. But you don't need a separate app UI just for Duo. The documentation has guides for Ionic Vertical Bars and framework-independent integration.
More room for what we can build with the web
All the plugins and libraries described here are open source under the MIT license:
- Native UI for Capacitor: @rdlabo/capacitor-native-ui
- Native UI for Cordova: @webview-native-ui/cordova-plugin
- Native ads: @capacitor-community/admob
- Shared coordination for plugin authors: WebView Native UI Engine
Building with web technologies doesn't have to mean giving up the native capabilities your app needs. You can build the content and behavior with the web, then bring in native UI where it makes sense. That's the choice I wanted to make possible.
After almost ten years of treating this as a constraint, I finally got past it. This was software development, but it felt like inventing something. I'm proud of this one!
If this made you think, “Wait, a web-based app can do that?”, I'm happy. If it gives you something you've wanted for your own app, even better.
What native UI have you wanted to use alongside your web content?
Adapted from my Japanese article on note, with the comics translated into English.











