4 ways to create cross-platforms apps using web technologies

PWA, Electron, Cordova / Ionic, React Native & NativeScript

We’re going to review in this article, 4 major ways to use web technologies to create cross-platforms applications. The idea is to help you decide when and how to re-use your web skills to have the highest reach possible, decide of the UI richness level you need and try to share your JavaScript code cross-devices as much as possible. It’s based on my latest customers’ meetings, internal discussions & projects I’m working on at Microsoft as well as my own researches on the web. Thanks to that, I’ve identified 4 possible paths: PWA, Electron, Hybrid apps and JavaScript Native driven approaches. We’re going to cover each of them and I will share their pros & cons.

Note: I’ve been working on preparing this content for 2 conferences I’ve delivered on this topic. One was in Ukraine for the Item 2018 conference (video here) and one was during the French MS Experiences 2018 event. As the feedback was positive and as people asked me for written content to share, I’ve decided to write this article. It’s also my very personal point of view I’m sharing. I’d love to discuss about your own point of view, feedback and experiences in the comments section!

Let’s start by the life of a front web developer today. It looks like this (mess?):


And it’s just a slice of what exists today. Obviously, you can’t know all those frameworks & tools. There isn’t a specific combination to rule them all neither. You will have to make a choice based on your preferences, needs, projects requirements and existing skills set of your team members. But the good news is whatever the stack you may have chosen, it’s a web stack. This will provide you additional benefits for cross-platforms support.


Let’s start by the obvious and main solution you should think of: Progressive Web App or PWA. If you still don’t know what a PWA is, here are good resources to read/watch:

– Our main entry point containing tons of good resources:  https://developer.microsoft.com/en-us/windows/pwa
Building Progressive Web Apps during BUILD 2018 by Jeff Burtoft
Progressive Web Apps on Google Developers
Progressive web apps on MDN


In a nutshell, a PWA is a web app using the latest available features to match as close as possible a native app. For instance, it can be installed on a desktop/mobile, will work perfectly offline and should use a modern UI approach. As it’s based on standards, it works cross-platforms on Windows, Mac, Android, iOS and could even fallback in any web browser on your Smart TV for instance!

The minimum viable you need to implement to start claiming you’re building a PWA is:

HTTPS. A PWA must be served over a secure channel.
Web App Manifest. A small piece of JSON describing how your PWA should behave or how to install it.
Service Worker. The core part of your PWA enabling a true offline scenario as well as boosting the performance of your web app.


I already covered in a previous article how PWA Builder can help you building this manifest and how I’ve been implementing it in my own PWA: Service worker, manifest and icons to make it a PWA.

By the way, my PWA, which turns out to be a WebGL/WebVR game, is available there: https://aka.ms/applescrusher. You can load it in any recent browser or device.

Note: you can read the full article: Using WebGL and PWA to build an adaptive game for touch, mouse and VR devices if you’d like to know how to build a similar PWA game.

If you’re opening my PWA on an Android device, you will have a similar experience as shown in this video:

Thanks to the Web App Manifest built by PWA Builder, the user can install the web app on his home screen. You can review the manifest on Github as well as the complete source code of this PWA game.

You’ll see I’ve decided to use a display property set to standalone. This will remove the border UI (the “chrome”) of the browser when the app will be launched from the home screen. The experience will then be different than the classical one you have inside the browser. I’m also forcing the orientation to be set to landscape. This is very useful in the case of games as the gameplay is often optimized for a specific orientation. Today, the usual trick is to use a media query to ask for the user to rotate the screen. You’ve probably already seen that. Thanks to the Web App Manifest, you can be sure that your app is using the right orientation directly.

Now that we covered the beauty of the manifest, let’s talk about the Service Workers. As a reminder, the Service Worker is a piece of JavaScript acting as a client proxy between your web page and the server. You first need to register it and once installed, it will be able to catch every fetch request sent by your web page to the network.


Based on your application’s logic and targeted experience, you have several possible strategies to manage the service worker’s cache. PWA Builder is providing various service workers ready to be used. You will probably have to adapt their code to perfectly match your needs, but it’s a good starting point.

As explained in my article, in my case, I’ve coupled a service worker with the usage of IndexedDB. The idea is to provide an offline experience that should be available just after the first complete load of the web page. Thus, if you manage to load my game on your mobile just once, even by switching to airplane mode immediately after, you will still be able to reload it. Just like a normal native app you would have installed from a store. In my case, the first load could be considered as the downloading process of an app from a store.

To highlight the result, I’ve done this second video to show you that this is working as expected:

As you can see, even without any network connectivity, the game can still be loaded from the browser. The service worker is getting the application files (JS, HTML, CSS and images) from its cache while the game engine is taking the game’s 3d assets from IndexedDB. I’m pretty proud that the IndexedDB layer I’ve written years ago for Babylon.js is still totally relevant for PWA 🙂

We just covered some of the main benefits provided by PWA via this use case. But there’s now a lot of PWAs out there on the web, and they have interesting feedback to share. For instance, if you go on: https://www.pwastats.com, you’ll learn that the PWA version of Twitter has managed to create a better user engagement.


And you’ll see several other great testimonials like this one.

However, even if the web platform has done impressive progresses in term of features exposed, there are still some limitations compared to native apps. This web site is interesting to give you an idea: https://whatwebcando.today/


As you can see, you can’t have access to NFC for instance. To go beyond this barrier, there are several possible solutions, the cross-platforms ones are exposed after.

In our case, at Microsoft, we offer you this step further on Windows 10. You can submit your PWA into the Windows Store for a better discoverability, possible monetization and to better integrate the OS paradigms by using some of its design language. You can also call the native WinRT APIs from JavaScript to go beyond of what the standard web specifications offer you today and break the barrier described just before.

Our main documentation entry point on PWA for Windows 10 is: https://developer.microsoft.com/en-us/windows/pwa. Please read our documentation explaining how this works: https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps/get-started.

PWA Builder can package your PWA for the Windows Store for you and we’re sharing some possible native integrations on it. But if you’d like to know what are the APIs you can call from JavaScript once hosted in a Windows 10 native UWP container, please read: Windows Runtime (WinRT) for JavaScript.

Some of our partners have published their app using this approach such as Twitter.


And to conclude on my own experiment, my game is also available in the Windows Store. I’ve explained in the dedicated section of my article the customization I’ve made for the Windows Store.



So, what are the PROs & CONs of a PWA you need to have in mind when you’ll think of your new app.


  • It’s just a web site!
    • Easy updates on the web server
    • Same code everywhere
    • Use your favorite framework (Angular, Vue, React) or language (TypeScript, JavaScript)
  • High reach / Any device
  • Indexable


  • Same UI/UX on all platforms
  • Don’t (always) have a full access to the platform / hardware

Indeed, even if you’re submitting your app to the Windows Store, the updates will be done on the web server hosting the PWA and will be immediately reflected on the user’s device. You will have to go through the validation process the first time, of course, but future updates will be much faster to push to the device than an update of a classic native app where a new package has to be pushed every time. If you don’t publish on a store, well, it’s just a web site again. Web is offering by far the highest reach possible today as every device has a web browser. It’s also easy to be indexed by search engines. Deep linking is part of the web whereas it could be something difficult to implement in a native app.

Of course, not everything is perfect using a PWA approach. You will have the very same UI/UX on all platforms. If you need to differentiate the UX on a specific platform to embrace its specific design language, this is not something easy. Also, as discussed before, you won’t always have a full access to the platform. If your app must discuss with a USB device for instance, this could be a blocker.

So, what are the options to resolve those cons?


You probably already used an Electron app without knowing it.


Visual Studio Code, Microsoft Teams, Slack, Skype or Discord are all based on Electron for instance, to name a few. You can view an exhaustive list of other apps using Electron here: https://electronjs.org/apps.

So why Electron is so popular and what does it offer? Electron can package your existing web app, which could already be a PWA by the way, and extends it on the desktop to break the barriers discussed in the previous section. It targets desktops only: Windows, MacOS & Linux. Electron can’t target the iOS or Android devices.

Electron is packaging your web app alongside with the full Chromium and Node.js binaries. It means that every time you’re installing an Electron app, it installs a full copy of Chromium and Node.js with it.


Note: Electron is a project created and maintained by Github. As Github has been acquired by Microsoft, it turns out Electron is a Microsoft project in a way now 😉

There used to be some important delta between the version of Chromium shipped with the Electron project and the official Chrome release. This has been reduced recently. For instance, the Electron 3.0 stable release has shipped in September 2018 with Chromium 66. You can find more recent releases of Electron shipping Chromium 68 or 69. Still, Electron is usually a couple of months behind the latest public builds of Chrome/Chromium. It means that you won’t be able to ship a code using the very latest features shipped with Chrome inside an Electron app. Keep that in mind. But you will just have to wait for a couple of weeks/months before it becomes available in the Chromium version shipped in the Electron branch.

An electron app can better integrate the desktop environment. As explained in their doc, you can set the recent documents, manage notifications or drag files out of the window for instance. You can also have access to the file system via Node.js as explained in this doc: Electron Application Architecture.

As shown in the diagram above, Electron is providing an abstraction layer via a set of APIs. You can then have a common JavaScript code targeting several platforms, the bridge is done by those APIs. But you can also use or write a native Node.js plug-in to make specific calls to the platform if needed. In the case of Windows 10 for instance, if you’d like to make native calls to the WinRT API, you can use this Node.js module: https://github.com/NodeRT/NodeRT. This is a project written & used by Slack to better integrate the Windows 10 platform.

On my side, I’ve decided to check, for fun, how far I could use Electron to create a copy of the default installed Calculator app which is written using UWP/XAML. First of all, to achieve the Fluent Acrylic effect / blurry background, I’ve found this NPM package: windows10-fluently-vibrancy for Electron apps.

Using the latest CSS features such as CSS Grid and Flexbox combined with some other tricks, I’ve managed to create something relatively closed to the design of the Calculator app. Look at this video comparing both versions:

So, who’s who? Smile

The first one is the UWP version and the second one is the Electron prototype I’ve built. You’ll see I’ve been able to do the compositing effect with the background to create the acrylic effect and even managed to partly implement the reveal effect on some of the buttons. The background compositing effect is done thanks to a call to a native Windows API. This was to illustrate you how Electron could embrace some of the platform’s specifics.

So as you can see, you can use Electron to share your code as much as possible cross-desktops, but you can still use the platform’s API to do a great job integrating its design language.


Again, to help you deciding whether Electron could be a good choice for you, here are the PROs & CONs:


  • Use your favorite web stack (front + node.js)
  • Controlled browser’s engine
  • Can interact with the system via native calls
  • Linux / MacOS / Windows easy targeted


  • Embed a full Chromium & Node.js
    • Size
    • Security updates of Chromium
  • Desktop only
  • Web UI

You can then take an existing website or PWA to embed it into an Electron app. You don’t have to learn anything specific on the front development side as it’s based on a very common stack all web developers know and love. As it’s embedding Chromium, you’re guaranteed to have a unique rendering & JavaScript engine to validate your code against. Compared to a standard PWA, you can go a step further by making native API calls through the Node.js layer.

However, there are some drawbacks to these obviously. First, the size of your installation package. Indeed, you won’t rely on the already installed browser of the platform as you’re shipping yours inside your package. This also means that you’ll be responsible for updating the embedded browser. If a security issue is discovered inside the Chromium version you’re shipping, your app could be a security breach for the user’s system. You must pay attention to this whereas with a classical PWA, this is the responsibility of the browser’s vendor to update & maintain his product you’re running on top of. At last, Electron is not targeting iOS / Android mobile platforms, it’s desktop only. Still, nothing prevents you to ship it as a PWA for desktops & mobiles and package it as an Electron app to offer a more advanced version for the desktops. At last, most of the time you’ll implement the same Web UI/UX of a regular web app even if I’ve demonstrated that nothing prevents you from embracing the platform design language.

Hybrid web app

Another way to push the actual PWA barriers is to use a hybrid approach. Hybrid means you’ll create a “native app” with a WebView component that will host your web app. Then, you’ll use a bridge between this WebView component and the native app to do native calls to the platform from your JavaScript code. The WebView could either take 100% of your app’s layout or part of it. In both cases, your app will be considered hybrid. You could for instance use only a small part of your app’s layout to embed a web map service. In our case, we will only talk about full layout WebView, we won’t talk about native components in this section.

There are 2 options to build a hybrid app:

  • Apache Cordova
  • Your own native app with a WebView inside it
    • XAML/C# or C++
    • Swift / Objective C
    • Java, etc

But if you use the second option, you will have to handle yourself the communication between the WebView and your JS code with the native code.

That’s why, the most obvious choice to consider is Cordova. You can learn more about Cordova: https://cordova.apache.org/ if you don’t know it yet. You’ll find some apps based on Cordova here: https://phonegap.com/app/.


Cordova is offering you a framework that will simplify the communication between your JS code living in the WebView and the native platform through plugins. Those plugins expose JS endpoints you can call from your code and then, the endpoint is making native API calls for you.

There’s a bunch of plugins available by default in Cordova providing various services like battery status, file access, vibration and so on. Features often not available in browsers. You can also search for existing plugins or write your own. The plugin must be written in the native language of each targeted platform: Objective C for iOS, Java for Android, C#/C++ for Windows, etc. So, writing a plugin requires specific skills.

Your web app code (HTML/CSS/JS), like Electron, can either be directly embedded in the app package or hosted on your web server.

Cordova then helps you to access the platform’s API from JavaScript. But what about the UX/UI you’ll build?

You have 2 choices for the UI:

  • Offer the same UI across all platforms as a regular web app or PWA
  • Embrace the UI of the targeted platform

As we’re running inside a WebView, embracing the mobile’s UI experience using pure CSS/HTML & JS is not something simple. Even a simple iOS or Android slider could need a lot of a work to be built with HTML/CSS and to mimic as close as possible the native control.

That’s why, you can use another framework on top of Cordova to help you on this part too. A current very popular one is Ionic: https://ionicframework.com/.


It offers a set of controls that will change their look/behavior dynamically based on the platform they’re running on. It uses a modern web component approach via Stencil.js. For instance, here’s the sample code running on 3 different mobile platforms (iOS/Android/Windows):


You can see the UI is adjusted to match the design language of each platform. Still, you have a unique HTML / JS code to maintain on your side. Even if the UX/UI proposed by Ionic is impressive, it’s still not completely identical to the native controls. But honestly, it will do the job most of the time for your users.

You can find a list of apps built on top of Ionic: https://showcase.ionicframework.com/apps/top.

Please note that Ionic is also currently working on a new approach named Capacitor to move away from Cordova. It uses their own hybrid solution instead. It can target iOS/Android, Electron & PWA.


Hybrid apps is then another interesting approach for you to consider but what are their PROs & CONs?


  • Can better integrate with the hardware / platforms via plugins than a PWA
  • Can have a look & feel very similar to the native controls thanks to Ionic…


  • … but it’s still not a pure native experience
    • Performance
    • UX

Cordova is often used to target mobile platforms, even if it can perfectly address also desktops one. You can check their interesting platform status: https://cordova.apache.org/docs/en/latest/guide/support/. Compared to Electron, it doesn’t ship a unique browser engine (Chromium) but rely on the already installed platform’s WebView (Safari based on iOS, Chrome on Android, Edge on Windows, etc.). It can also make calls to the native platform and break some of the barriers of a regular PWA. Regarding UX, you can offer to your users a native-like UI thanks to Ionic.

But it’s still not a true native experience. The performance will vary based on the rendering / JavaScript engine of the target platform. A slow device could clearly show some differences versus the native UI controls. At last, even if the job done by Ionic is impressive, the UX will never be perfectly matching the native UX.

Still, thanks to this approach, we can perfectly imagine having a PWA shared & extended across-platforms. A lot of the code, if not all, will be shared. Having a non-perfect mobile UX could totally make sense for your targeted users.

JavaScript-driven Native

Up to now, we’ve found various ways to do calls to the platform APIs (Windows 10 PWAs, Electron or Cordova) but never managed to find a perfect solution for the UI part. Still, those solutions could be used to share almost 100% of the JS code as well as the HTML/CSS for the UI.

The JavaScript-driven Native is using the native controls of the platforms and will offer this perfect UI solution you may looking for. Its approach consists of keeping only the JavaScript code in common with other platforms (it could be Electron or PWA) and targets mobile platforms only. You will have to rewrite the views to use a new language targeting both iOS and Android native controls at the same time. Those views, as well as the business layer, will be driven by JavaScript.

Today, 2 frameworks are using this approach: Telerik NativeScript and Facebook React Native. Both are very similar in their approach. The choice between one of them will be driven by your existing skills. You’re already using Angular or Vue? NativeScript will probably be a natural path. You’re a master of a React.js? React Native is the way to go. But again, there’s no absolute choice. You should evaluate deeply this approach as well as both frameworks implementing it before making your final decision.

Telerik NativeScript

On my side, even if I have basic React.js knowledge, I decided to first play with NativeScript. Based on their FAQ: “NativeScript framework enable developers to use pure JavaScript language to build native mobile applications running on the major mobile platforms – Apple iOS, Google Android and Windows Universal. The applications UI stack is built using native UI components and because of that no compromises with the User Experience with the applications is done.”.

If you’re a developer like myself, you probably already wonder how this magic works? Well, check out their doc: NativeScript – a Technical Overview.

NativeScript is not only providing an XML-based UI declaration to abstract the instantiation of native controls of each platform, it also abstracts the platform API via a JavaScript SDK. For instance, look at this file access done from a single JavaScript line of code:


You see that it will be translated to the appropriate Java or Objective C code. Something Electron is also offering, but in the desktops’ world.

Here are cool diagrams illustrating the execution flow in detail:


Let’s now see a video I’ve made illustrating their XML-based language to dynamically create native UIs on both an iPhone and Android phone at the same time.

The layout is described using an XML language you’ll have to learn. But it’s obvious, even more if you’ve already built native applications (XAML or Android are using XML approaches). The controls (Label, StackLayout or GridLayout shown in the video) are mapped to their corresponding native controls on iOS/Android. The structure of the project looks like an Angular one with .HTML, .CSS and .TS files. The styling of the native controls can be achieved via CSS. In the demo, I’m showing an Angular approach. This means that an Angular developer will be able to reuse his habits / skills building a NativeScript application.

Both phones are connecting to the NativeScript playground via websockets thanks to the QR code generated. Then, you can live edit the UI code in the browser and it will be reflected by instantiating native controls on the connected phones. As you can see, the sliders, switches, datepicker & calendar controls are offering the native & perfect UX of both platforms. There won’t be any difference from a native app on this side for your user.

React Native

React Native is very similar to NativeScript, offering the same set of tools to remotely test/debug for instance. The idea is to write your business and view logic in JavaScript and remove the major need for native UI expertise. The business logic will be shared across all platforms in JavaScript: iOS, Android, Web and macOS. The philosophy of Facebook behind is “Learn Once, Write Anywhere”. React is not something that easy to learn. I often discuss about this with the developers I’m meeting during conferences. But once you understand how it works, it’s a beautiful approach. Bonus: spending time learning React.js will offer you an easy path to React Native.

React Web components are written in JSX. The props & states are bound to the DOM. Here’s a sample React Web component:


React Native components are also written in JSX. But this time, the props & states are bound to the native views. Look at the same component written for React Native:


It means that you’ll be able to re-use your React.js skills, but you’ll obviously have to rewrite to views for React Native to use the appropriate language for native components.

Some famous apps are built using React Native:


At Microsoft, we’re also using React Native for Microsoft Teams on iOS and Android. There’s an interesting article to read about Microsoft Teams architecture: Microsoft Ignite Live Blog – BRK3118 – Microsoft Teams Architecture: “The Teams client architecture shares a code base between Windows and Mac, which allows Microsoft to ship updates to both platforms at the same time. Also interesting is that Microsoft is moving from Angular to React for the desktop client. Angular was their choice back in the days, because that seemed the right choice then, but moving forward a shift to React provides code share possibilities between the desktop and mobile clients.”


This architecture diagram is interesting. It shows a possible solution to create a complete cross-platforms application using web technologies. MS Teams decided to use React.js for the Web version of the app, to package and extend it on Windows & Mac using Electron and to ship it on iOS & Android using React Native. Thanks to that, we’re able to share almost a unique JavaScript code base.


So, is JavaScript Native the ultimate solution? Of course not! But there are great positive points.


  • Rich Native UI & perfect platform integration
  • Re-use your JavaScript business layer / logic
  • Target iOS & Android with a unique UI code


  • Need to learn the native controls / syntax
  • Debugging can be much more complex than just hitting F12
  • Less reach than PWA

This approach is currently very popular and has great positive aspects. But it still fairly new and feedback from teams / products using it should be read before going into this direction. For instance, the blog posts series of Airbnb is a must read: React Native at Airbnb. They’ve ended up moving away from React Native and they’re explaining why. Of course, you’ll find also very positive testimonials on the official React Native page such as the one from Pinterest: Supporting React Native at Pinterest. Our own Microsoft Teams group seems very happy with their choice.

JavaScript Native is very promising and offer a perfect solution for reaching native UX using web technologies in background. But it’s at a cost: a possible complexity added by the required layers on top of your JS code to manage the communication with the native platform. It means that you will probably have sometimes to extend the chosen framework to build your own native component. Or you will fight with debugging issues. Still, it seems to fulfill his promises most of the time: targeting the 2 mobile platforms, thanks to a single UI code, it uses the native UX / UI controls of each, powered by a single JS business layer code shared across all platforms.


I’d like to start by this sentence: “The Web is for audience reach and native apps are for rich experiences. Both are strategic. Both are valuable. So when it comes to mobile, it’s not Web vs. Native. It’s both.”.

It comes from Mobile Web vs. Native Apps or Why You Want Both written by Luke Wroblewski in 2016.


Today, you can target both reach and rich experiences using web technologies. The web can now build high quality apps on any device.

To my point of view, PWA should be considered first. This is what I’m recommending to partners & customers I’m meeting during my Microsoft life. It has the highest reach, the highest portability and it’s now mature enough to be used in production without issue. Its full support is widely available, and the Progressive part will make it compatible with older browsers anyway. At Microsoft, we’re going to invest a lot into PWA in the future.

PWA has of course 2 major limitations: you won’t have a native UX and you won’t have a full access to the hardware / platform (except on Windows 10). If it’s a blocker, and if you’re an Angular/Vue or React developer, there are some interesting combos to investigate. Based on your specific needs, I would recommend one of those:

  • PWA + JavaScript Native for mobiles
  • PWA + Electron for desktop
  • PWA + Electron + JavaScript Native

There are the common patterns I start to frequently see. But again, there’s no absolute answer. You’ll have to first think about the UX you’d like to provide to your user, the level of portability you may need, the specific platform/native integration needs you may have. Finally, you will have to consider the existing skills of your team and the potential learning curve associated with a new technology introduced. But I’ve met several teams made by former C# or Java developers having switched to those approaches without specific difficulties, just after a couple of months of ramp up.

I hope this will help some you having a better understanding of what you can and what you can’t do today with web technologies. Feel free to share your own experience and feedback in the comments’ section!

Leave a Reply

Your email address will not be published. Required fields are marked *