Creating an Electron + React.js Template that Works!

Michael Michailidis
5 min readFeb 26, 2018

--

(Code on GitHub)

A few months ago I got exposed to Electron by building a blockchain app, and beyond the basic examples that are provided by the original Electron team, I encountered only grief!

I was trying to combine Electron with popular frameworks like React and Angular, with a preference to the first due to its simplicity, and although there are many popular boilerplates, I could not find what I wanted. My main problem was this: They all used the native react tools to generate a project and retro-fitted Electron afterward. What you ended up with was a standard client-server architecture with a development server needed to serve the pages as if it was a web app… only it wasn’t! The result, and you can use any of the links above to see this, was that once you fired a simple:

const p = fetch('https://google.com');

You got an error in the console:

Fetch API cannot load https://google.com/. Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Now, that is correct, and should be expected, if you are running a web app inside a browser as it prevents a form of hacking called cross site scripting by restricting “Ajax” calls to the domain the page was served from. Electron apps, on the other hand should not have such restrictions, and the running of a web server as a separate process is totally useless and can only lead to confusion.

So, frustrated with what I found, I built my own: Electrate, and here I will show you how I did it.

Begin with a Template that is Sane

The good people form Electron have provided a great starting point in the form of a simple template. Clone it on your local repo

git clone https://github.com/electron/electron-quick-start my-app
cd my-app
npm install
npm start

This will give us an elementary template with no build scripts but with the base code to get an Electron app running. Production-grade applications usually end up with a complicated folder structure, but for starters, I will create a src/ folder and move index.html and renderer.js, renamed as index.js. I will create an index.css and link it to the html. Finally, add an app container div.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="./index.css">
<title>Electrate</title>
</head>
<body>
<div id="app">
</div>

<script src="./index.js"></script>
</body>
</html>

With a small change to main.js so that it points to the correct file:

mainWindow.loadFile(path.join(__dirname, 'index.html'))

We can now run the application from NPM

npm run start # or npm start

Transpiling ES6 with Babel 7

As most of React user code is written in a format called JSX, we first need to add Babel so we can compile it down into standard Javascript.

npm i @babel/core --save-dev # this will install babel 7.0 

Babel works through presets, which also need installing:

npm i @babel/preset-env @babel/preset-react --save-dev

When it runs, it looks for its configuration in a file named .babelrc, so create in on the root and add:

{ "presets": ["@babel/env", "@babel/react"] }

Babel needs to run before any code executes and the best way schedule that is through a build tool. My preference is Gulp, although you could use any other.

npm i gulp gulp-babel --save-dev # basic gulp
npm i gulp-concat gulp-clean-css --save-dev # plugins

The idea is to compile all the files that exist in our source directory into plain old Javascript and move them into a single folder.

When Electron runs it reads from package.json and looks for the “main” key. It runs the file that is included there so we will set it to point to the place where main will be moved after compilation

"main": "app/main.js",

This main script will create the window that will load the index.html which in turn will call our first JS file that will inject the React app.

Create a gulpfile.js at the root of your project and add the tasks

const exec = require('child_process').exec;
const gulp = require('gulp');
const babel = require('gulp-babel');
const css = require('gulp-clean-css');
// 1. Copy the index.html as is
gulp.task('html', () => {
return gulp.src('src/index.html')
.pipe(gulp.dest('app/'));
});
// 2. Compile CSS file and move them to the app folder
gulp.task('css', () => { // 2.
return gulp.src('src/**/*.css')
.pipe(css())
.pipe(gulp.dest('app/'));
});
// 3. Compile JS files and move them to the app folder
gulp.task('js', () => { // 3.
return gulp.src(['main.js', 'src/**/*.js'])
.pipe(babel())
.pipe(gulp.dest('app/'));
});
// 4. Start the electron process.
gulp.task('start', gulp.series('html', 'css', 'js', () => { // 4.
return exec(
__dirname+'/node_modules/.bin/electron .'
).on('close', () => process.exit());
}));

This task (start) will be run from npm and through the package.json

"scripts": {
"start": "gulp start"
},

Finally Adding React.js

Now we are ready to add React.

npm i react react-dom --save-dev

Let’s create our basic app container file as a React Component in app.js.

import React from 'react';

export default class App extends React.Component {
render() {
return <div><h2>Hello Electrate</h2></div>;
}
}

The index.js file will inject the base component into the body:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app.js';

window.onload = () => {
ReactDOM.render(<App />, document.getElementById('app'));
};

Just for clarity, what we have ended up doing is this:

Electrate Architecture Diagram

Packaging the app

There are mainly two options for packaging an Electron app and we will go with the second, Electron Builder (the other being Electron Packager).

npm i electron-builder --save-dev

For this tool we need to create a build folder where we will put our our app icon. We need to point the tool to the folder with the code to be compiled through the package.json by adding:

"build": {
"appId": "com.karmadust.electrate",
"files": [
"app/**/*",
"node_modules/**/*",
"package.json"
],
"publish": null
}

Then we will call it after we have compiled and moved the files there through gulp. So in your .gulpfile add:

gulp.task('release', ['copy', 'build'], () => {
return exec(
__dirname+'/node_modules/.bin/electron-builder .'
).on('close', () => process.exit());
});

Through npm:

"scripts": {
"postinstall": "install-app-deps",
"start": "gulp start",
"release": "gulp release"
},

Adding Livereload

It would be a shame if we left our template without an autoreload. So let’s add gulp-livereload plugin. Have a look at the project files to see how it’s set up.

Enjoy!

--

--

Michael Michailidis
Michael Michailidis

Written by Michael Michailidis

I am a software engineer working in Node.js + iOS with an eye for blockchain applications!