Takeaways on Building a React Based App with Electron
Tags: electron, javascript, node
Published: 2018-07-12 14:48:15

---
title: Takeaways on Building a React Based App with Electron
published: true
description: Building and Publishing a React Based Electron App
tags: Electron, JavaScript, Node.js
---

![](https://thepracticaldev.s3.amazonaws.com/i/n65l6kt5q5v9ad90t321.png)

Earlier this year, [Stream](https://getstream.io) launched [Winds 2.0](https://getstream.io/winds), an open-source and native application for macOS, Windows, and Linux, which provides an entirely new way to consume RSS feeds and Podcasts. It was our first time building a native application, so we chose to go with [Electron](https://electronjs.org/), a framework for creating cross-platform applications.

In addition to Electron, we leveraged React, as it has an astoundingly large community, is open-source, and is easy to develop with. If you’d like to contribute or see additional information on Winds, have a look at our [GitHub repo](https://github.com/getstream/winds) for the project. 

> If you haven’t used Winds, you can sign up at [https://getstream.io/winds](https://getstream.io/winds). Or, if you just want a visual, below is a screenshot of Winds 2.0 running inside of Electron: 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image5.png?w=1024&h=1024) 

We all know how fast developer tools move these days. Unfortunately, a side effect of this rapid innovation is outdated content on the web – sometimes by several months or years – even for a popular tool with a strong following like Electron. We knew pretty much immediately that we’d be on our own for this project. Luckily, we took some notes and we’re sharing them here to bring you up to speed with our findings.

### To ensure you don’t get lost, here’s a quick rundown on the components that we’ll be talking about in this post:

*   Using Xcode to generate `.p12` files for signing your distributions
*   How a provisioning profile can be created on [https://developer.apple.com](https://developer.apple.com) (this verifies that your application is published by you and **YOU** only)
*   What entitlement files are and how [entitlements.mas.plist](https://github.com/electron-userland/electron-osx-sign/wiki/3.-App-Sandbox-and-Entitlements) say which permissions your app needs (e.g. network, file, settings, etc.)
*   Code signing/distribution with electron-builder
*   How [electron-builder](https://github.com/electron-userland/electron-builder) works and calls [Xcode’s codesign utility](https://developer.apple.com/support/code-signing/) behind the scenes
*   ASAR files and what they are
*   Application Loader and how it’s used to send your distribution to Apple
*   The actual store listing is defined in iTunes Connect
*   Keys for macOS are generated on [Apple’s website](https://developer.apple.com)

With the latest version of Node.js installed ([currently](https://nodejs.org/en/) @ v10.6.0), let’s dive right in and get started.

1\. Up and Running with React
=============================

For React, we’re going to use [Create React App](https://github.com/facebook/create-react-app) (CRA), a React scaffolding tool build and maintained by Facebook. The beauty of CRA is that it requires zero configuration on your behalf (unless you eject from CRA, which is outlined [here](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-eject) – please read as it’s important to know why and when you should and should not eject from CRA).

### Install Create React App Globally

`yarn global add create-react-app`

### Create Example Application with Create React App CLI

```
npx create-react-app example
cd example
yarn start
```

> Note: [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) comes with npm 5.2+ and higher

### View Your Example App in the Browser

Then open [http://localhost:3000/](http://localhost:3000/) and you’ll see our basic boilerplate React app. 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image8.png?w=1024&h=1024)

Easy, right? You’ve now bootstrapped your React application with only a few commands and are ready to move to the next step!

2\. Prepping for Electron
=========================

Next, let’s go ahead and start prepping our React application for use with Electron. What we found to be the best setup for this is to do the following (make sure that you are in the example directory):

### Install Electron

`yarn add electron --dev`

### Move into the `public` directory and create a new file called `electron.js`:

`cd public && touch electron.js`

### Populate the contents of your `electron.js` file with the following:

```
const { app, BrowserWindow, shell, ipcMain, Menu, TouchBar } = require('electron');
const { TouchBarButton, TouchBarLabel, TouchBarSpacer } = TouchBar;

const path = require('path');
const isDev = require('electron-is-dev');

let mainWindow;

createWindow = () => {
	mainWindow = new BrowserWindow({
		backgroundColor: '#F7F7F7',
		minWidth: 880,
		show: false,
		titleBarStyle: 'hidden',
		webPreferences: {
			nodeIntegration: false,
			preload: __dirname + '/preload.js',
		},
		height: 860,
		width: 1280,
	});

	mainWindow.loadURL(
		isDev
			? 'http://localhost:3000'
			: `file://${path.join(__dirname, '../build/index.html')}`,
	);

	if (isDev) {
		const {
			default: installExtension,
			REACT_DEVELOPER_TOOLS,
			REDUX_DEVTOOLS,
		} = require('electron-devtools-installer');

		installExtension(REACT_DEVELOPER_TOOLS)
			.then(name => {
				console.log(`Added Extension: ${name}`);
			})
			.catch(err => {
				console.log('An error occurred: ', err);
			});

		installExtension(REDUX_DEVTOOLS)
			.then(name => {
				console.log(`Added Extension: ${name}`);
			})
			.catch(err => {
				console.log('An error occurred: ', err);
			});
	}

	mainWindow.once('ready-to-show', () => {
		mainWindow.show();

		ipcMain.on('open-external-window', (event, arg) => {
			shell.openExternal(arg);
		});
	});
};

generateMenu = () => {
	const template = [
		{
			label: 'File',
			submenu: [{ role: 'about' }, { role: 'quit' }],
		},
		{
			label: 'Edit',
			submenu: [
				{ role: 'undo' },
				{ role: 'redo' },
				{ type: 'separator' },
				{ role: 'cut' },
				{ role: 'copy' },
				{ role: 'paste' },
				{ role: 'pasteandmatchstyle' },
				{ role: 'delete' },
				{ role: 'selectall' },
			],
		},
		{
			label: 'View',
			submenu: [
				{ role: 'reload' },
				{ role: 'forcereload' },
				{ role: 'toggledevtools' },
				{ type: 'separator' },
				{ role: 'resetzoom' },
				{ role: 'zoomin' },
				{ role: 'zoomout' },
				{ type: 'separator' },
				{ role: 'togglefullscreen' },
			],
		},
		{
			role: 'window',
			submenu: [{ role: 'minimize' }, { role: 'close' }],
		},
		{
			role: 'help',
			submenu: [
				{
					click() {
						require('electron').shell.openExternal(
							'https://getstream.io/winds',
						);
					},
					label: 'Learn More',
				},
				{
					click() {
						require('electron').shell.openExternal(
							'https://github.com/GetStream/Winds/issues',
						);
					},
					label: 'File Issue on GitHub',
				},
			],
		},
	];

	Menu.setApplicationMenu(Menu.buildFromTemplate(template));
};

app.on('ready', () => {
	createWindow();
	generateMenu();
});

app.on('window-all-closed', () => {
	app.quit();
});

app.on('activate', () => {
	if (mainWindow === null) {
		createWindow();
	}
});

ipcMain.on('load-page', (event, arg) => {
	mainWindow.loadURL(arg);
});
```

3\. Modifying our package.json File
===================================

Once you’ve created the `electron.js` file, we’ll need to go ahead and modify our `package.json` file in order to point to and execute the correct files and commands. Your entire file should look like the following: 

```
{
	"name": "example",
	"version": "1.0.0",
	"description": "Building and Publishing a React Based Electron App From Scratch",
	"private": false,
	"author": "Nick Parsons <nparsons08@gmail.com>",
	"license": "MIT",
	"homepage": "./",
	"main": "public/electron.js",
	"keywords": [
		"Example",
		"React",
		"Electron"
	],
	"scripts": {
		"dev": "yarn react-scripts start",
		"build": "react-scripts build",
		"start": "concurrently \"cross-env BROWSER=none yarn react-scripts start\" \"wait-on http://localhost:3000 && electron .\"",
		"pack": "electron-builder --dir",
		"dist": "npx build --x64 --macos --win --linux --c.extraMetadata.main=build/electron.js -p always"
	},
	"dependencies": {
		"electron-is-dev": "^0.3.0",
		"electron-publisher-s3": "^20.17.2",
		"react": "^16.4.1",
		"react-dev-utils": "^5.0.1"
	},
	"devDependencies": {
		"react-scripts": "1.1.4",
		"concurrently": "^3.6.0",
		"cross-env": "^5.2.0",
		"electron": "^2.0.3",
		"electron-builder": "^20.18.0",
		"version-bump-prompt": "^4.1.0"
	},
	"build": {
		"appId": "com.your-domain",
		"compression": "normal",
		"productName": "Example",
		"directories": {
			"buildResources": "build",
			"output": "dist"
		},
		"mac": {
			"icon": "assets/icon.icns",
			"type": "distribution",
			"target": [
				"pkg",
				"dmg",
				"mas"
			],
			"publish": {
				"provider": "s3",
				"bucket": "example-releases",
				"path": "releases"
			},
			"category": "public.app-category.news"
		},
		"mas": {
			"entitlements": "assets/entitlements.mas.plist",
			"entitlementsInherit": "assets/entitlements.mas.inherit.plist",
			"provisioningProfile": "assets/embedded.provisionprofile"
		},
		"win": {
			"target": "nsis",
			"icon": "assets/icon.ico",
			"publish": {
				"provider": "s3",
				"bucket": "example-releases",
				"path": "releases"
			}
		},
		"linux": {
			"icon": "assets/icon.png",
			"target": [
				"snap",
				"AppImage"
			],
			"description": "Example",
			"category": "Network;Feed",
			"publish": {
				"provider": "s3",
				"bucket": "example-releases",
				"path": "releases"
			}
		}
	}
}
```

> Note: Run `yarn install` in order to install the packages in the package.json file.

Now you can simply run `yarn start` and... 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image1-1.png?w=1024&h=1024) 

Your application is now running inside of an Electron wrapper!

4\. Preparing for Distribution
==============================

We’re not going to dive into how to build an application in this section; however, we will touch base on how you begin to package your app for distribution to various stores such as the macOS and [Snapcraft](https://snapcraft.io/) (Linux) stores.

### Adding Logos

You’ll also want to create an assets directory in the public directory. Once created, you’ll need to drop the following files into the directory (we’ll be referencing them in a bit).

*   icon.ico

*   icon.png (256x256px)

*   icon.icns

Here’s a quick command to create the directory: 

`cd ../ && mkdir assets`

> Note: If you don’t have images, you can use the icons from Winds icons [here](https://s3.amazonaws.com/winds-hosted/icons/icons.zip).

### Generating Keys

To get up and running for macOS, you will need ~6 certificates provisioned by Apple in the Developer Console – follow these instructions:

1.  Head over to [https://developer.apple.com](https://developer.apple.com) and login
2.  Go to the “[Certificates, Identifiers & Profiles](https://developer.apple.com/support/certificates/)” section
3.  Select the drop-down and choose macOS
4.  Click the + button and generate the certificate types below

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image4-1.png?w=1024&h=1024) 

Once complete, download the certificates. When you open them, they will automatically be stored in your keychain.

### Adding Entitlements Files

Now that we’ve added our images to the `assets` directory, let’s go ahead and add our `entitlements` files. These are important when signing your application for release. 

**Inside of the assets directory, run the following command:**

```
cd assets && touch entitlements.mas.plist && touch entitlements.mas.inherit.plist
```

Then, populate the `entitlements.mas.plist` with the following content: 

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.application-identifier</key>
    <string>XXXXXXXXXX.com.your-domain</string>
	<key>com.apple.security.network.client</key>
	<true/>
	<key>com.apple.security.files.user-selected.read-write</key>
	<true/>
  </dict>
</plist>
```

This entitlement file specifies that you need access to the network in addition to file access (for drag and drop).

> Note: You will need to populate the “XXXXXXXXXX” value with your Apple provided ID, and the rest of the string with your domain in a package naming convention (e.g. XXXXXXXXXX.com.example). You will need to obtain this from Apple at [https://developers.apple.com](https://developers.apple.com) (it costs around $99/yr. to be an Apple developer).

And `entitlements.mas.inherit.plist`:

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.inherit</key>
    <true/>
  </dict>
</plist>
```

Last, we’ll need to create our `embedded.provisionprofile` for macOS and save it in the `assets` directory. Apple uses this file to verify that the application is legitimate. Follow the steps below to generate a provisioning profile for your application:

1.  Head over to [https://developer.apple.com](https://developer.apple.com) and login
2.  Go to the “Certificates, Identifiers & Profiles” section
3.  Select the drop-down and choose macOS
4.  Click the + button in the top right-hand corner
5.  Select “Mac App Store” under the “Production” section
6.  Click continue
7.  Select “Mac App Distribution”
8.  Follow the instructions for generating a “CSR”

Once complete, you’ll have yourself an official `embedded.provisionprofile` to sign your application! Here’s what the various screens look like for reference: 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image3-1.png?w=1024&h=1024)

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image7.png?w=1024&h=1024)

Now it’s time to double check the build settings within our `package.json` file. The file contains build configurations for Linux, Windows, and macOS. We don’t use every setting, so if you’d like to see what all is available, visit [https://www.electron.build/configuration/configuration](https://www.electron.build/configuration/configuration). 

Here’s our build configuration for Winds:

```
"build": {
		"appId": "com.your-domain",
		"compression": "normal",
		"productName": "Example",
		"directories": {
			"buildResources": "build",
			"output": "dist"
		},
		"mac": {
			"icon": "assets/icon.icns",
			"type": "distribution",
			"target": [
				"pkg",
				"dmg",
				"mas"
			],
			"publish": {
				"provider": "s3",
				"bucket": "example-releases",
				"path": "releases"
			},
			"category": "public.app-category.news"
		},
		"mas": {
			"entitlements": "assets/entitlements.mas.plist",
			"entitlementsInherit": "assets/entitlements.mas.inherit.plist",
			"provisioningProfile": "assets/embedded.provisionprofile"
		},
		"win": {
			"target": "nsis",
			"icon": "assets/icon.ico",
			"publish": {
				"provider": "s3",
				"bucket": "example-releases",
				"path": "releases"
			}
		},
		"linux": {
			"icon": "assets/icon.png",
			"target": [
				"snap",
				"AppImage"
			],
			"description": "Example",
			"category": "Network;Feed",
			"publish": {
				"provider": "s3",
				"bucket": "example-releases",
				"path": "releases"
			}
		}
	}
```

5\. Debugging & Resources
=========================

Electron is a rather new technology, and although it powers hundreds if not thousands of [applications](https://electronjs.org/apps) – most well known among the development community are [Atom](https://atom.io/) and [Slack](https://slack.com/) – it still has bugs. There is an active ecosystem around the project creating useful tools such as [electron-builder](https://github.com/electron-userland/electron-builder), but these tools also have their own set of bugs. We’ve run into countless error messages, blank screens, rejected app store submissions, etc., but it never made us stop exploring what Electron has to offer. 

During the process, we found a good number of great debugging tools and other reading material that we felt compelled to write down to share in this post. If you’re running into an issue, you’ll likely find the answer in one of the following resources:

*   [Electron Builder](https://github.com/electron-userland/electron-builder)
*   [Releasing an App on the Mac App Store](https://medium.com/@flaqueEau/releasing-an-electron-app-on-the-mac-app-store-c32dfcd9c2bd)
*   [Mac App Store Submission Guide](https://electronjs.org/docs/tutorial/mac-app-store-submission-guide)
*   [A Beginners Guide to iOS Provisioning Profiles](https://blog.theodo.fr/2017/02/a-beginners-guide-to-ios-provisioning-profiles/)
*   [iOS Code Signing & Provisioning in a Nutshell](https://medium.com/ios-os-x-development/ios-code-signing-provisioning-in-a-nutshell-d5b247760bef)
*   [Verifying that a Package is Signed](https://www.jamf.com/jamf-nation/articles/299/verifying-that-a-package-is-signed)
*   [How to open and repackage OSX .pkg files](https://snippets.aktagon.com/snippets/669-how-to-open-and-repackage-osx-pkg-files)
*   [Package MAS Error](https://github.com/electron-userland/electron-builder/issues/2719)
*   [List of Categories](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW8)
*   [RB App Checker Light](https://itunes.apple.com/us/app/rb-app-checker-lite/id519421117?mt=12)

6\. The ASAR File & What it does
================================

One question we had when using electron-builder is what the ASAR file did and why it was packaged in our deployment. After much digging, we found that an ASAR file, or archive rather, is a simple tar-like format that concatenates files into a single file that allows Electron to read arbitrary files from it without unpacking the whole file. 

At the end of the day, it’s really just a read-only map of what files are within the Electron build, allowing Electron itself to know what’s inside. This can sometimes trigger various anti-virus scanners. With that said, you can pass the `--unpack` option and some files will not be packed. Doing so will create two files; `app.asar` and `app.asar.unpacked`. 

If you’re interested in a technical deep dive on ASAR files, head on over to the electron-builder page on application packaging [here](https://electronjs.org/docs/tutorial/application-packaging).

7\. Deploying to Users
======================

> Note: This section requires that you have [Amazon S3](https://aws.amazon.com/s3/) up and running with [awscli](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) (`pip install awscli && aws configure`) associated with your account. The name of our bucket will be `example-releases` and files will be placed inside of a directory called `releases` within that bucket.

Once this is done and ready to go, you can now deploy to users! Simply run `yarn build` and [electron-builder](https://github.com/electron-userland/electron-builder) will run all of the necessary commands in order to package up the correct bundles for each operating system. Once complete, run `yarn dist` and it will begin uploading (using the credentials from `aws configure`) the packages to Amazon S3 where you can then link users to for downloads. 

Here’s a sneak peek at what our AWS S3 bucket looks like: 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image2-1.png?w=1024&h=1024) 

The easiest way to upload your application to the macOS Store is via the Application Loader which is built right into Xcode. Simply go to **Xcode** \> **Open Developer Tool** \> **Application Loader** 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image9.png?w=1024&h=1024) 

Once open, you will be asked to login: 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image10.png?w=300&h=300)

> Note: You must have an Apple ID associated with a valid Apple Developer Account. Additionally, you must temporarily disable 2FA as it doesn’t play nicely with the Application Loader. 

Once logged in, you will be prompted with a selector where you can choose the proper file to upload. 

![](https://getstream-blog.imgix.net/blog/wp-content/uploads/2018/07/image6.png?w=1024&h=1024) 

> Note: When choosing a file, ensure that you navigate to the mas directory. The application within mas is the only package that will work properly for that macOS store. 

When uploading to the macOS store, it’s likely that you’ll have to go through several iterations with Apple to dial in the details. Apple is very picky, for a good reason – they don’t want applications chock full of errors in the app store. It’s just a part of the learning process, so don’t let it get you down. 

> Bonus: If you want to deploy to the Snapcraft store, check out the docs on their website at [https://docs.snapcraft.io/build-snaps/](https://docs.snapcraft.io/build-snaps/). It’s a rather straightforward process, and you already have the Snap file (inside of your `dist` directory) ready to upload!

Wrapping Up
===========

Hopefully, you’ve learned a thing or two. If you have questions or comments, please drop them in the comments below. If you’d like to get in touch with me directly, I’m always available on Twitter – [@NickParsons](https://twitter.com/@nickparsons). Stay tuned for more posts about Winds. Best of luck in your future React and Electron endeavors!