Create a desktop app in electron (with react)

10 min read
July 8, 2021

To get an overview on how to startup your first Electron project, this blog post is a great way to start. We’re going to explain how to get your first Electron project up and running.

Why Electron

In this day and age searching for project related information can be a hassle. You are constantly bombarded with information, and hit by hype trains for frameworks and technologies that someone says are the best for your new app. 

Sometimes you don’t know where to start. 

This blog post is not going to be like that. That’s because we are biased towards the Electron framework.

Not because we don’t follow the latest trends, but because we want to help you reduce the white noise while searching for a solution.

The first question you ask yourself is which technology should I use to find that solution. 

We assume that this question is already answered and you chose to go with Electron. Even though Electron has competitors it has stayed on the market for several years.

Benefits of using Electron

  • High data security
  • Accessibility
  • High performance
  • Simplified management
  • Reusable framework
  • Compatibility
  • Interaction with with web UI/UX tools

That’s why it also has a lot of example projects and good documentation. It’s being maintained regularly, and a lot of projects have been completed using the framework.

Just with that info you know you’re heading in the right direction.

Let’s get an overview of our tech stack

Electron

Our main technology is, of course, Electron. Let’s imagine this is your first time getting to know its tech, and you want to familiarize yourself with the API by bootstrapping your first simple project. For the ones that want to know more, here’s the official Electron documentation.

React

As we know, Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS. In this blog post, we’re going to use React library for building the client version of our app. You can find out more in their official documentation.

Webpack

Since this will be a simple project we’re going to use webpack for bootstrapping it all together. Webpack is a powerful tool in building your applications and this is a great starting point. Again, here is their official documentation.

Other

For the sake of making a complete starting point for your project we’re going to be using other technologies such as:

TypeScript – to have type safe Javascript code

SCSS – for styling your app

Yarn – package manager

Babel – javascript compiler

Webpack, babel, and TypeScript setup

Electron development is essentially Node.js development. So, if you don’t have it on your machine go to this quick start guide and install it. 

Then, create a custom project folder and run yarn init command in the root folder to initialise your package.json file. You will get a prompt for information, but when you get to the main  part of the init setup just input the following line:

"main": "./dist/main.js"

This points out our main entry point of the electron project.

After the yarn setup, we’re going to install Electron, webpack and babel with a single command that you can paste in your terminal which is positioned in the root of your project:

yarn add -D electron --dev --exact \
webpack webpack-cli@3.3.12 webpack-dev-server \
babel-loader @babel/core @babel/preset-env \
@babel/preset-react @babel/preset-typescript

Note: For the sake of this blog post and the errors that were happening (which may be fixed) we’re going to be using a specific version: webpack-cli@3.3.12.


Next, we need a tsconfig.json setup so create that file in the root of your project. When you do that you can put inside the file the following code:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": [
      "dom",
      "es2015",
      "es2016",
      "es2017"
    ],
    "allowJs": true,
    "jsx": "react",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
  }
}

Basically, tsconfig.json is specifying the root files and the compiler options required to compile the project. We’re not gonna go into details, but if you want to learn more about tsconfig you can check out the link.

Furthermore, we need to add a babel configuration. Create a babel.config.js in the root of your folder with the following code:

module.exports = {
  plugins: ["@babel/plugin-transform-async-to-generator"],
  presets: [
    '@babel/preset-env',
    '@babel/preset-react',
    '@babel/preset-typescript'
  ]
}

Also, we’re not going into details with this file and if you want to learn more about the babel plugins and presets you can check out the following links:

Plugins

Presets

HTML app entry point

We need a html file which we will load in as an entry point for our Electron app on start. Create an index.html file in the root of your project with the following markdown:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Your new Electron App</title>
  </head>
  <body></body>
</html>

Electron Webpack config

To configure the webpack config for our Electron side of the application we need to create a file in our root folder named webpack.config.js. For that file you can use the following code:

const path = require('path');
module.exports = {
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  devtool: 'source-map',
  entry: './electron/main.ts',
  target: 'electron-main',
  module: {
    rules: [
      {
        test: /\.(js|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].js',
  },
};

In this code snippet we can see that target is a specific environment that webpack will compile for. In our case we’ve named it electron-main. Also, the entry is actually the entry point of the electron app. In our example we’ve made an electron folder in the root folder with a file called main.ts that has the main application code.

Electron app

The main code for creating an instance of our Electron app is in the main.ts file and the source code of that file looks like this:

import { app, BrowserWindow } from 'electron';
import * as path from 'path';
import * as url from 'url';
let mainWindow: Electron.BrowserWindow | null;
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
    },
  });
  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools();
    mainWindow.loadURL(`https://decode.agency:4000`);
  } else {
    mainWindow.loadURL(
      url.format({
          pathname: path.join(__dirname, '../index.html'),
          protocol: 'file:',
          slashes: true
      })
    );
  }
  
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}
app.on('ready', createWindow);
app.allowRendererProcessReuse = true;

In this code snippet we can see that we use the createWindow function to make a browser window of the application.

Pay attention to the if else condition where if we run the application in a ‘development’ environment open the browsers’ devtools and load the localhost of the React application which webpack setup we will see in the next section.

Also, if we don’t run the ‘development’ environment we just load up the index.html file that we defined earlier.

React setup

The UI part of our app will be made with React library. For this part we’re going to do a similar config setup as for the Electron app.

First we will need to set up our library by installing it. Use this snippet and run it in your terminal positioned in the root of your project.

yarn add react react-dom @types/react @types/react-dom

Then we will set up our webpack config by creating a file in the root of our project named webpack.react.config.js. But first, we will need to install the html webpack plugin. Use your terminal again and run the following code command snippet:

yarn add -D html-webpack-plugin

Next, you can add the following code to our webpack.react.config.js file:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
    mainFields: ['main', 'module', 'browser'],
  },
  entry: './src/index.tsx',
  target: 'electron-renderer',
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.(js|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },
  devServer: {
    contentBase: path.join(__dirname, '../dist/renderer'),
    historyApiFallback: true,
    compress: true,
    hot: true,
    port: 4000,
    publicPath: '/',
  },
  output: {
    path: path.resolve(__dirname, '../dist/renderer'),
    filename: 'js/[name].js',
    publicPath: './',
  },
  plugins: [
    new HtmlWebpackPlugin(),
  ],
};

For the last part of our UI we will need a simple .tsx file with our code for the grounds of our UI side of the app. You can create an src folder inside the root of your app and there you can create an index.tsx file as your main file for the UI part of the Electron app.

In the file you can put the following code:

import React from "react";
import ReactDom from "react-dom";
const mainElement = document.createElement("main");document.body.appendChild(mainElement);
const App = () => {
  return (
    <section>
      <h1>Hi from a react app</h1>
    </section>
  );
};
ReactDom.render(<App />, mainElement);

Styling

As a final touch, you can add styling grounds for UI part of the application. To do that, first you can install the packages

yarn add -D sass-loader sass css-loader style-loader resolve-url-loader

Then your need to add a new rule (module → rules) into our webpack.react.config.js

{
  test: /\.s[ac]ss$/i,
  use: [
    // Creates `style` nodes from JS strings
    "style-loader",
    // Translates CSS into CommonJS
    "css-loader",
    // Needed package for resolving relative paths in url()
    // needs to be before sass-loader in loading chain
    // more info on https://github.com/webpack-contrib/sass-loader#problems-with-url
    "resolve-url-loader",
    // Compiles Sass to CSS
    "sass-loader",
  ],
},

Next we’ll create our styling folder structure in src. You can do something like this:

scss
    ├── base
    │   ├── _colors.scss
    │   ├── _index.scss
    │   └── _typography.scss
    └── main.scss

Try and change the color of h1 to gray and test that our installed packages are working well and that babel compiles it correctly. Add the following code to the files respectively:

/ colors.scss
$gray: #888ea7;
// typography.scss
h1 {
  color: $gray;
}
// index.scss
@import "colors";
@import "typography";
// main.sccs
@import "base/index";

Lastly, we’re going to import our main.scss styling file in index.tsx

import "./scss/main.scss";

Start your app

Once we’ve set up all the project architecture the last thing we need to do is to add our terminal commands to scripts part of our project package.json file.

"scripts": {
    "dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .”,
    "dev:react": "NODE_ENV=development webpack-dev-server --config webpack.react.config.js --mode development"
},

To run the app, you can run the said script commands in your terminal respectively.

yarn dev:react
yarn dev:electron

Conclusion

Every time you want to learn something new you try and search up information about new tech.

The goal of this article was to combine all the needed information for making a simple Electron app with React library. We compiled the needed information into a concrete application bootstrap without any finished libraries or products. 

We’ve used a simple webpack to strap our project and I’m sure that made you dig deeper to learn more about webpack itself combined with babel.

Making our main.ts file was the main goal so you can use it as a jumping-off point for your own project. 

We wanted to guide you through all the oversaturated information that you can find online.

The goal is to encourage you to get up and running with your first Electron app on top of which you can build and create new stuff and keep learning. So, great job and good luck with new features.

If you have any questions, or want to work with an experienced team, feel free to contact us at business@decode.agency

Categories
Written by

Tarek Saghir

Frontend Developer

Related articles