How to Package Your Multi-platform Electron App
Tags: electron, javascript
Published: 2021-12-08 07:34:31

---
title: How to Package Your Multi-platform Electron App
published: true
date: 2020-09-06 11:47:58 UTC
tags: electron, javascript
canonical_url: https://www.turtle-techies.com/how-to-package-your-multiplatform-electron-app/
---


*Written by [Andres Acevedo](https://www.turtle-techies.com/author/andres-acevedo/)*

In part [one](https://www.turtle-techies.com/multi-platform-desktop-app-with-electron) and [two](https://www.turtle-techies.com/securing-your-electron-app) of these Electron app development series, we created a basic text loader, and then addressed some security issues. 
But at the end of the day, we have not yet experienced the best feature of Electron apps:  

Their ability to run on multiple platforms.  

In this tutorial, we will use the codebase that we ended up with in the last part. [you can get it here](https://github.com/mran3/Secure-Text-File-Loader).

**Note:** I usually provide a link to a repository with the completed project at the end of the tutorial, but for this one I think is important that you have the `package.json` at hand to compare it with yours and find any possible differences in case you run into problems.

You can find the app configured to build on macOS, Windows, and Linux here:  
https://github.com/mran3/Text-File-Loader-Build

## macOS windows management
Before delving into packaging our app, let's do a small adjustment to our code to respect the conventions of the macOS platform, where usually applications remain open even if they don't have any active windows. 
We can implement that behavior easily with electron by adding the following code to **app.js**.

```javascript

app.on(
    "window-all-closed",
    () => process.platform !== "darwin" && app.quit() // "darwin" targets macOS only.
);


```

Other possible values for `process.platform` are:
* freebsd
* linux
* openbsd
* win32 (applies to any Windows).

> If you are on a mac, relaunch our app and check that when you close the window, the app remains on the dock. 
> Don’t worry about the electron icon, we will solve that next.

## Icons
Also, if we are talking about distributing our app and provide a good user experience, we can not do it with the Electron logo as the icon. Create or find an icon of your liking for your app, I am going to use this one:  


![The icon used for this tutorial](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/78pegdq4j2zguisqe13r.png)

macOS uses a `.icns` format, Windows requires `.ico` and Linux prefer `.png`, fortunately, you can create icon files from a `.png` using online tools like  https://cloudconvert.com/png-to-icns .

Once you have them in both formats, create a new folder in the root of the project (I called it **assets**) and put both icon files there.

## Packaging and distribution toolset


To distribute your app with Electron, you need to package it for each operating system you want to target. 
The goal is to generate a `.exe` file for Windows, a `.app` for macOS, a `.deb` for Debian-based Linux distributions, and so on.

[Electron Packager](https://github.com/electron/electron-packager) is the official Electron tool to help us convert our source code to a bundle specific for macOS, Linux, or Windows. 

For Windows, Electron Packager will create a functional `.exe` along with a bunch of `.dll` and config files. 
Although you can put this together on a `.zip` file and send it to your users, that does not provide a great user experience. 
So we can go a step ahead and consider not only how to package our app but also how to distribute it. 

That's when [Electron Forge](https://www.electronforge.io/) comes into play, allowing us to create nice step by step installation wizards for our application. 
Internally Electron Forge uses Electron Packager, so it is not necessary to install them separately, let's run the following command to install them both:  

```shell

npm install @electron-forge/cli -D

```
> **Note**  
> Be careful not to install electron-forge (without the @), as this will install electron-forge v5 which has problems creating apps for the latest versions of macOS.  
> Besides that, the official documentation relates to v6.  


Then, let's import our app into the Electron Forge workflow by running on a terminal located at the root of our project:

```shell

npx @electron-forge/cli import

```
Have in mind that if you want to start a new Electron app with Forge included, you don't have to import your project, instead, you can start a new project with the `create-electron-app` command (more info on the official Electron Forge documentation: https://www.electronforge.io/).

Although in theory, you could generate all your packages from one platform, that requires installing and configuring a lot of tools (i.e. wine and mono on mac to be able to build for Windows), so I recommend generating each executable in its own platform.


### macOS
We are all set for making our executables.  
I will start with macOS, but you can skip ahead if you are interested in the process for [Windows](#windows) or [Ubuntu Linux](#linux).

Open your `package.json` and in the scripts section, add one new entry:  

```javascript

    "make-mac": "npx @electron-forge/cli make --platform darwin",

```

Also, modify the name field so it **does not contain spaces or uppercase letters**. 
I am sure you don't want to present your app to your users with that hyphens on it, to avoid that, create a new field below `name` named `productName` and put whatever name you like.  
The beginning of your `package.json` file should look similar to this:

```javascript

{
  "name": "electro-text-reader",
  "productName": "ElectroReader",
  "version": "1.2.0",
  "description": "Multi platform Text Loader",
  "main": "main.js",
  "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
     "make-mac": "electron-forge make --platform darwin"
  },

```

Now we need to add some configuration for Electron Forge, inside your `package.json` there should be a `config` field:  

```javascript

"config": {
    "forge": {
      "packagerConfig": {
        "icon": "./assets/icon"
      }
    }
  }

```

As you can see, the icon extension is not specified, but Electron Packager (under the hood) will add the correct extension (.ico or .icons) depending on the platform.  

Now run the script we created by running the following command on your terminal:  
```shell

npm run make-mac

```

At the end of the process, you should end up with a new folder named out.  
Inside of it there should be two folders one with a long name containing the `.app` file generated by Electron Packager and the other named **make** contains a `.zip` file that you can distribute to your users. 
Uncompress, run and enjoy: 

![Our installed macOS app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6fqbdc26drkd0qp4dxje.png)

*Doesn't it look nice?*  

> **Note**  
> For each platform, you can have different makers that produce different types of distributable files, maker-zip is the default maker for macOS, but others allow for example exporting to DMG files   
> (you can read more about it here: https://www.electronforge.io/config/makers/dmg ).

### Ubuntu (Debian) Linux {#linux}
To create a `.deb` package, we will need to make sure that our system has two utilities installed: `fakeroot` and `dpkg`, these are not `npm` packages, but actual applications of the operating system.  
To install them, run the following commands on your terminal:  

```shell

sudo apt install dpkg
sudo apt install fakeroot

```

Add a new entry inside your scripts section with the following content:  

```javascript

"make-linux": "npx @electron-forge/cli make --platform linux --targets deb"

```

> You could just execute `npx @electron-forge/cli make` without the platform or targets arguments and electron forge will try to identify in what platform it is running and try to create the corresponding packages. 

Getting the icon to work on linux required some additional steps.  
Make sure the configuration of maker for debian (in your `package.json`) specifies what icon to use:

```javascript


{
          "name": "@electron-forge/maker-deb",
          "config": {
            "icon": "./build/icon.png"
          }
}


```

This will set the icon of the app, but for it to appear in the dock, we will need to jump off from our `package.json` for a moment and modify our `main.js` adding the icon to the call of new `BrowserWindow`.

```javascript


const win = new BrowserWindow({
    width: 800,
    height: 600,
    icon: path.join(app.getAppPath(), 'build/icon.png'),
    webPreferences: {
      worldSafeExecuteJavaScript: true,
      contextIsolation: true,
      preload: path.join(app.getAppPath(), 'preload.js')
    }
  })

```

Now on a terminal let's run:

```shell

npm run make-linux

```

If everything went well you should have again an out folder with two sub-folders.  
One with a bunch of files (the output of electron-packager), and make where you will find a nice `.deb` file ready to be double-clicked and install our beautiful app:  

![Our installed Linux app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m50tcc7qfq984shd0i2x.png)

*The app installed on Ubuntu*  


One configuration that builds your app without problem in one platform, might need to install additional packages on another. 
For example, macOS does not have a problem if the description field on `package.json` is not present, but it **is required** for Debian.


### Windows  {#windows}

As you might guess, we need to add a new entry to `npm` scripts. 
Why not test the platform inferring capabilities of Electron Forge and call it without parameters:  

```javascript

"make": "npx @electron-forge/cli make"

```

Squirrel is the default maker for Windows. 
It is intended to be lightweight and provide a hassle-free experience to the user, not requiring admin permissions or long installation wizards.  
As a side note, this makes Squirrel great for creating apps that can run on a pen drive.


Right now Squirrel will work, but it won't generate a nice Desktop icon, but for that, we just need to add a simple line to our `main.js` file:  

```javascript

if(require('electron-squirrel-startup')) return;

```

Run `npm make` from the terminal, install your application, test it, and if everything goes right, edit your CV and add "Developer of multi-platform desktop applications". 😉  


![Our installed Windows app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/omoy3uyobphh61r9967w.png)
*Electron app installed on Windows*  



If you run into any problems, remember that you can find the completed project on:  
https://github.com/mran3/Text-File-Loader-Build