Bringing a react web app to the desktop

With the growing demand for more native-like web applications, companies are looking to desktop web container solutions like Electron, Tauri and OpenFin to evolve beyond the browser. Bringing a react web app to the desktop opens up possibilities like improving performance, integrating with native notifications, window management and an overall more flexible experience for apps with advanced user interfaces.

The potential here is exciting because it allows businesses to leverage their existing web assets, tech stacks and developer expertise to deliver an experience that feels more powerful and responsive on the desktop.

In this article, I will walk through bringing a web app to the desktop using frameworks such as Progressive Web App, OpenFin, Electron and Tauri. I will also use progressive enhancement techniques to enable a the basic experience when viewing the app in a browser and then detect when running in a desktop container for a more immersive experience on the desktop.

Setting the Target 

For comparison, I'll be targeting desktop web app parity with one of my existing native desktop apps, Desktop MP3. The goal is to have the web desktop version achieve as much parity with the native desktop version as possible.

The existing native app supports functionality unique to desktops such as: installing an app icon, browsing for a local folder of music, maintaining a reference to the local folder across sessions, and even advanced windowing techniques including transparent native window regions and hiding the native window's titlebar.

Native app running on the desktop

Starting With a Web App

For the starting point of bringing a web app to the desktop, I have created a web-based version of the app using React with Redux. In the next steps, I will start with the web-based version as I walk through the steps of bringing it into parity with the native desktop version.

Web app running in a browser tab

How does the baseline version of the web app compare to the native app?

Desktop icon: By dragging the website icon to the desktop, we can launch the web app from the desktop later on. However, as seen in the screenshot above, the app icon uses a system icon instead of the one provided by the web app.

Opening a local folder: In order for a web app to open a folder from the local desktop, we can use FileSystem API. However, the browser shows a security prompt when selecting the local folder.

Browser showing security prompt when choosing a local folder

Maintaining reference to local folder: The FileSystem API does not allow for saving a reference to a local folder across sessions. So the next time the user closes and reopens the browser to the web app, the user will need to browse for the folder and accept the security prompt again.

Extra-small native window dimensions At the time of this writing Chrome and Edge both prevent the width and height of the browser window from going smaller than 500 pixels wide by 300 pixels tall, resulting in extra unintentional padding when the app is switched to collapsed mode.

Browser preventing window from going smaller than 500x300 pixels

Native transparent window regions: Browsers do not support native desktop transparent regions. As a tradeoff, we can used a gradient background to start with and employ progressive enhancement techniques to switch to a transparent background when the app detects it is running in a desktop container which supports it.

Hiding the native window titlebar: Browsers do not support hiding the native window titlebar.

Using a Progressive Web App (PWA)

By setting up the proper configuration to run the web app as a Progressive Web App, we can get a step closer to a native desktop experience.

Web app running as a Progressive Web App (PWA)

How does the progressive web app compare to the native app?

Desktop Icon: When installing a PWA, the browser provides an app icon that can be added to the desktop. Unlike the regular browser shortcut, this desktop icon actually uses the icon from the web app.

Opening a local folder: In order for a progressive web app to open a folder from the local desktop, we can use the same FileSystem API as a regular webapp. However, since the app is still technically running in a browser window, the browser still shows a security prompt when selecting the local folder. 

PWA showing browser security prompt when selecting a local folder

Maintaining reference to local folder: The FileSystem API does not allow for saving a reference to a local folder across sessions. So the next time the user closes and reopens the browser to the web app, the user will need to browse for the folder again. However, unlike a regular web app which shows the security prompt for each session, the PWA only shows the prompt on the first session -- unless the user chooses a different local folder in a new session.

Extra-small native window dimensionsJust like the regular browser window, the PWA window won't go as small as desired, leaving extra unintended padding. 

PWA preventing window from going smaller than 500x300 pixels

Native transparent window regions: Since the PWA is running in a browser window, it still cannot support native desktop transparent regions, so for this version we are still using a gradient background to start with and employing progressive enhancement techniques to switch to a transparent background when the app detects it is running in a desktop container which supports it.

Hiding the native window titlebar: Browsers do not support hiding the native window's titlebar. However, as a tradeoff for this version, we can set the titlebar color match the look-and-feel of the rest of the app by adding an entry to the PWA manifest file.

PWA using a custom titlebar color to match the background of the app

Using OpenFin

By targeting OpenFin as a desktop container for the web app, we can provide users with an easy way to install the app from the website and run it on the desktop. The OpenFin container on the desktop runs the app in a Chromium window. 

Desktop web app running in OpenFin container

How does the OpenFin desktop web app compare to the native app?

Desktop icon: Using OpenFin's icon settings we can specify the url of the icon in addition to whether to install the icon to the desktop and/or the start menu.

Opening a local folder: When attempting to read the list of files that the user selected in the browse window, the app immediately crashes. OpenFin documentation states that "Access to the filesystem in OpenFin is restricted exactly as it is on the web." It specifically mentions using the FileSystem API or drag and drop. After trying a few ideas, I was not able to successfully load the files from my local machine. More research needs to be done.

OpenFin crashed when using FileSystem.API

Maintaining reference to local folder: Since I didn't get the folder to load without crashing, I couldn't test maintaining a connection. More research needs to be done.

Extra-small native window dimensions:  OpenFin allows the native window go smaller than 500x300 which allows the app's collapsed mode dimensions to appear as intended -- unlike the PWA or regular browser windows.

OpenFin allows the window to be extra small

Native transparent window regions: Through the OpenFin transparent manifest file property, we can enable transparent regions on the native window.

Hiding the native window titlebar:  Through the OpenFin frame and hasShadow manifest file properties, we can turn off the native window's titlebar and frame. By using -webkit-app-region: drag on the app's own titlebar div element, we can enable dragging the window using the app's custom titlebar.




Notes on OpenFin

OpenFin provides a special URL that lets users easily launch a compatible web app from the browser using a specially formatted link. If users don't have OpenFin already installed, there is a button to install it.

OpenFin installer page

If the user has OpenFin already installed, visiting the special link shows browser prompt to launch the web app on the desktop. Users can check the box to always allow it and avoid future prompts.

OpenFin prompting to launch the web app


Using Electron

Using Electron, we can ensure that the rendering engine and javascript engine that the app was tested with is the same rendering engine and javascript engine it will run on once deployed to end users. 

Desktop web app running in Electron container

How does the Electron desktop web app compare to the native app?

Opening a local folder: Electron ships with NodeJS. So instead of using FileSystem API to load a local folder, which shows a browser security prompt, we can use Node to load a playlist from the local machine without interrupting the user with a security prompt.

Maintaining reference to local folder: Using Electron's appPath API togethe with NodeJS's filesystem api, we can save and retrieve settings across sessions. This way the user doesn't have to browse for the folder again each time they launch the app.

Extra-small native window dimensions:  Electron allows the native window to be extra small to accommodate the app's collapsed mode.

Native transparent window regions: Through the Electron transparent property, we can enable transparent regions on the native window.

Hiding the native window titlebar:  Through the Electron frame and hasShadow properties, we can turn off the native window's titlebar and frame. By using -webkit-app-region: drag on the app's own titlebar div element, we can enable dragging the window using the app's custom titlebar.

Notes on Electron

Electron apps ship bundled with their own copies of NodeJS and Chromium, so the framework itself is generally larger in file size and memory usage than most other frameworks.

The file size of the Electron version of the app on macOS is 217 MB.

Filesize of Electron app


For one window, there are 5 processes running using around a total of 130MB of RAM.
 
Memory used by Electron app

Using Tauri

Tauri works in a similar way to Electron but uses Rust instead of Node for native code, and instead of shipping a copy of Chromium with the app bundle, it uses the operating system's built-in webview (webkit on macOS and WebView2 on Windows).



How does the Tauri desktop web app compare to the native app?

Desktop Icon: Using Tauri's bundle.icon property, we can specify our own desktop icon.

Opening a local folder: I didn't get a working example in place, but my plan would be to use tauri::dialog::FileDialogBuilder in rust to present a native file browser, and then pass the list of songs to the frontend for display.

Maintaining reference to local folder:  I didn't get a working example in place, but my plan would be to use rust system level code to save the user's preferences, including the selected folder path, and then retrieve that value again upon app startup.

Extra-small native window dimensions:  Tauri allows the native window to be extra small to accommodate the app's collapsed mode.

Hiding the native window titlebar: Through the decorations property in Tauri config, we can turn off the native window frame.

Native transparent window regions: Through the transparent property in Tauri config, we can enable native window transparency. Warning: Enabling transparent windows in Tauri on macOS requires the macos-private-api feature flag which causes the app to not be allowed in the app store.

Alternatives

FinSemble

FinSemble is a smart desktop platform that includes out-of-the-box services for application docking, interapp communication, and workflow linking.

WebUI

WebUI allows us to choose from several different languages for the system level code, and from multiple browsers or the webview component for the user interface layer. 

Did we achieve parity with native app?

Using a Progressive Web App, we got taste for what could be possible, but it still has browser prompts or window restrictions that aren't applicable to native apps.

Using OpenFin we came close to parity with the native app with transparent regions, window sizing, etc. but due to the app crashing when FileSystem API was used, more research needs to be done for an alternative method of loading local files.

Using Electron app we were able to achieve parity with the native app in most regards. Even though it worked for this example, every app is different and has unique requirements that may or may not be compatible with Electron.

Screenshots

Below is a screenshot displaying five of the different app versions explored in this article: web app, progressive web app, OpenFin desktop web app, Electron desktop web app, and native app.

Screenshots comparing web app, desktop web apps, and native



Related post: Cross-platform desktop app filesize comparison

-
Anthony Tietjen is a software engineer with 15+ years experience spanning desktop, mobile and web development. Husband and father, with a passion for music. Connect with Anthony on LinkedIn and X/Twitter.




Comments

Popular posts from this blog

Adobe® Flex® Combobox: Selecting an Item Without Using SelectedIndex

Parameters Sent by TinyWebDB in App Inventor for Android