Convert Vue.js app to Native desktop app using Electron

In this post I am going to convert our Vue.js QReader app we build earlier into a native desktop app using Electron, gone are the days when if you wanted to create an app for a desktop you needed to know languages like C++, .Net or Objective C. With tools like electron you can build a fully native and cross platform for desktops using your web skills JavaScript, HTML & CSS. Let’s get started with some intro about Electron.

About Electron

Electron is a framework for creating native and cross-platform applications with web technologies like JavaScript, HTML, and CSS. It takes care of the hard parts and gives you API so you can focus on the core of your application. Electron is an open source library developed by GitHub Electron accomplishes this by combining Chromium and Node.js into a single runtime and apps can be packaged for Mac, Windows, and Linux.

Get started with starter kit

Since we will be just wrapping our web app into an Electron web view we can start with the kit provided by Electron, let’s open the terminal and run following commands, I assume you already have Node.js and NPM installed.

# Clone the Quick Start repository
$ git clone https://github.com/electron/electron-quick-start

# Go into the repository
$ cd electron-quick-start

# Install the dependencies and run
$ npm install && npm start

Folder Structure of Electron

Here is our app folder structure.

folder-structure electron starter kit

Index.html is the default page and main.js is main process entry point, I will explain main and renderer process later in this post.

Load our App in Electron Window

electron-hello-world

Now by default electron is loading a local index.html file, if your app is local you thats fine, but in our case, we want to use our already running app, so we will load the URL. Let’s open the main.js and tweak the createWindow() function on line 14 to load the URL.

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 600,
    minWidth: 480,
  })

  // and load the index.html of the app.
  // mainWindow.loadURL(url.format({
  //   pathname: path.join(__dirname, 'index.html'),
  //   protocol: 'file:',
  //   slashes: true
  // }))

  // and load the url of the app.
mainWindow.loadURL('http://qreader.qcode.in')
...

Now if you npm start in the terminal you should see our app loaded from url.

Main & Renderer Processes

Let’s dive into core concept of processes, so what is a process you might ask? In operating system level an instance of a computer program that is being executed is called a process. For example, if I start an Electron application and then check the Activity Monitor in macOS or Task Manager on Windows, I can see how many processes are associated with my Electron program.

Main process

The main process is responsible for creating and managing BrowserWindow instances and various application events, If you know vue.js you can think it like root component.  You can register global shortcuts, create native menus and dialogs, respond to auto-update events, and more. As name suggest our main process will be main.js file in this case. Most of Electron APIs are available in the main process with all node.js modules you installed.

Renderer Process

The renderer process is responsible for running the user-interface of your app, a web page which is an instance of webContents. All DOM APIs, node.js APIs, and a some of Electron APIs are available in the renderer.

Here is the list of API for all processes as of Electron v1.6.7.

API in Different Processes
Main Process Renderer Process Both Processes

Enough with the theory, let’s complete our app.

Fix white splash screen on load

The think is when the main process creates window it shows on screen but it can take a little while to completely initialize web view and fetch the URL can also take some time, that’s why it shows white splash, we can fix it by changing a color of the background to match our app or we can listen for page ready event.

Let’s modify new BrowserWindow() by passing show: false, which will not show the window, we will add a listener for ready then we will ask window to come on screen.

// Create the browser window.
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 600,
    minWidth: 480,
    show: false
  })

....

// Show when ready
  mainWindow.once('ready-to-show', () => {
     mainWindow.show()
 })

If your app is heavy (needs lots of data preloaded on launch) it can take some time to load the app, which will feel like slow launch, user will not see anything happening until app loads, one way you can keep the user engaged by building a splash screen which will show loading state initially.

App Name

By default when you launch the app it will display Electron in the menu bar, but we need it to be QReader, we can change it by updating your packages.json and adding productName field which will be used to name it later when we generate our app file.

...
"productName": "QReader",
"version": "1.0.0"

App Icon

In order to assign a custom icon you will need a PNG 1024 x 1024px square icon, I am using this artwork. There are plenty of image converter utility out there, but I prefer ICONVERTICONS  which does all the conversion we will need, .ico for windows, .icns for mac and .png will work for linux. Here is the icon I am going to use:

QReader App Icon

Once you downloaded all the converted icons paste them into below folder structure. Create assets folder in root first.

assets-folder-structure-electron-app

Now we have the icon, next, we will need to use it in our app. let’s pass icon in new BrowserWindow configuration.

mainWindow = new BrowserWindow({
    width: 1000,
    height: 600,
    minWidth: 480,
    show: false,
    icon: path.join(__dirname, 'assets/icons/png/64x64.png')
  })

Open target=_blanks links in New window

If you open a feed link, it should open in the browser window, but in our current app as you can see it is opening in a new window which is not we want.

We can fix it by running listening for new-window event on the webContents. Add this in main.js.

...
// and load the url of the app.  
mainWindow.loadURL('http://qreader.qcode.in')

// Open all target="_blank" link in external browser
let webContents = mainWindow.webContents;
    webContents.on('new-window', function(event, url){
      event.preventDefault();
      electron.shell.openExternal(url);
});
...

That fixes this issue, now we need a launcher for app, lets build the .app file for mac, you can also build for window and linux.

Generate Electron Package

Lets pull the electron-packager as dev dependency from npm.

# for use in npm scripts
npm install electron-packager --save-dev

# for use from cli
npm install electron-packager -g

Now we have it, lets generate package for mac.

Mac Build

electron-packager . --overwrite --platform=darwin --arch=x64 --icon=assets/icons/mac/icon.icns --prune=true --out=release-builds

It will generate the .app file into release-builds/QReader-darwin-x64 folder.

Linux Build

electron-packager . --overwrite --platform=linux --arch=x64 --icon=assets/icons/png/1024x1024.png --prune=true --out=release-builds

Windows Build

electron-packager . --overwrite --asar=true --platform=win32 --arch=ia32 --icon=assets/icons/win/icon.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Electron Tutorial App\"
Here are options details for packager

overwrite: replaces any existing output directory when packaging.

platform: The target platform to build for

arch: the architecture to build for

icon: sets the icon for the app

prune: runs npm prune –production before packaging the app. It removes unnecesary packages.

out: the name of the directory where the packages are created.

Now you can run the app by just clicking on app icon. and you can see you the name of app is also changed from Electron to QReader.

QReader-electron-app

Conclusion

I have covered only the basics in this post, I will suggest you must install the API Demos app from Electron if you really wanted to learn more. with electron its very easy to build native desktop app, if you know the html, css, and javascript you are all set. Let me know what you have created with Electron, if you have any question let me know in comments.