Electron app with Webpack, Bootstrap, FontAwesome and JQuery - A complete guide
Tags: webdev, webpack, tutorial, javascript
Published: 2021-02-15 11:29:42

## **tl;dr**
A step-by-step tutorial to build from scratch an [electron app](https://www.electronjs.org/) together with [Bootstrap](https://getbootstrap.com/), [FontAwesome](https://fontawesome.com/), [JQuery](https://jquery.com/) and bundled with [Webpack](https://webpack.js.org/)! 🎉

----

# ⚓ Introduction

As I was building an electron app for a [project](https://aliceandbob.io), I though it might be useful for other to share my work. It is not always easy to understand (Webpack can be sometimes complicated..) and I ran into a lot of issues! So I wrote this tutorial in order to help you to start building your own electron app and configuring Webpack together with Bootstrap, FontAwesome and JQuery.

I hope this tutorial will be a good basis for you to start with! 😊

👀 Already wanna see the final result? Or a production ready app built with the process described below? Go and check [aliceandbob.io](https://aliceandbob.io) or head to the [GitHub's page](https://github.com/aliceandbob-io/aliceandbob-desktop) of the project. 

{% github aliceandbob-io/aliceandbob-desktop no-readme %}

For the repository of the sample app we will build together, go, fork and star the repo below:

{% github theolazian/electron-webpack-bootstrap-fontawesome-jquery no-readme %}

As you may have seen, I chose a quite descriptive name for this app 😉

----

# 🗺️ Our objectives

For this tuto, we will create an electron app from scratch. Electron allows us to build cross-platform desktop apps with JavaScript, HTML, and CSS.

We will also add:
- Bootstrap 5, 
- FontAwesome 5, 
- JQuery, as well as 
- Custom `scss` and `js` files to our project. 

The whole project will be bundled with [Webpack](https://webpack.js.org/)!

For that, we will need to configure Webpack and add loaders to it in order to process our images, `js` and `scss` files. I'll explain everything later on!

Finally, we will bundle our app with Webpack and build it for Windows, MacOs or Linux.

There are plenty of tutos on the Internet, but I tried to reach a clean process of doing things in order not to get (too much) lost. 😉

----

# ⚙️ Prerequisites

Depending on your system, starting an electron app might be a bit complicated and you may need to install packages and dependencies in order to make it run. But not worries, it's worth it! 

I am on Windows and I use WSL (version 2), which runs Ubuntu-20.04. I will show you a few tips for people in the same situation along the way.

First of all, you may want to install `node` and `yarn`, so head to the [download page](https://nodejs.org/en/download/) of Node and install the version you need. 

Then, install `yarn` as follows:

```
npm install --global yarn
```

----

# 🌱 Step by Step initialization

Lets's go step by step together through the process! 

## 1️. Electron-forge

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/f3cslf4gua6jvfx6tpmu.png)

[Electron-forge](https://www.electronforge.io/) is a complete solution to package and build a ready-for-distribution Electron app. So let's use it!

## 2. Creating the app

Initializing a new project with Electron-forge is quite straight forward. You only need to run `yarn create electron-app my-app`.

💡 **But remember**, we want to use Webpack and Electron-forge actually gives us a boilerplate for that. So do not hesitate and start your project by running the following command:

```
yarn create electron-app my-new-app --template=webpack
```

You can replace `my-new-app` by the name you want.

Then, run `cd my-new-app` to move into your project folder.


## 3. Running the project

Here, you just need to run the following command to start running your first electron app! 🚀

```
npm start
``` 
or 
```
yarn start
```

>**Hint**: On WSL, you may encountered a futex utility error. In that case, what works for me was installing electron specifically from the PowerShell command line tool like so:

>1. Head to your project directory from the PowerShell command line tool `cd path/to/your/repo`,
>2. Run `npm install --save-dev electron`,
>3. Go back to the WSL command line running Ubuntu and run again `yarn start`.

>You might have to do that everytime you install new packages to your electron app... Otherwise you can install packages through the PowerShell command, and do the rest of the work on WSL. A bit tiresome, I confess, but it works 😬

Now that we created an electron app with electron-forge webpack installer, the Webpack plugin has been added automatically to our app. Find this line in `package.json`: `@electron-forge/plugin-webpack`.

If not (which would be weird!), go ahead and install it by yourself:

```
yarn add @electron-forge/plugin-webpack --dev
```

Thanks to this plugin, we can use the standard Webpack tooling to compile both your main process code and your renderer process code.

As per the electron-forge documentation, your `main` entry in your `package.json` file needs to point at "./.webpack/main":

``` json
// package.json

{
  ...
  "main": "./.webpack/main",
  ...
}
```
>Note that you might need to add the "." at the begining of the line if not already done.

And we will add the following in our `main.js` file:

```JS
// main.js

...
const mainWindow = new BrowserWindow({
  // add these 3 lines below
  webPreferences: {
    preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
  }
});
...
```

Now we're free to move on! 🐌

----

# 🗂️ Structuring the app

At this point, your app tree should look like that:

``` bash
📁 my-new-app
├── .git
├── .webpack
├── node_modules
├── src
│   ├── index.css
│   ├── index.html
│   ├── main.js
│   └── renderer.js
├── package-lock.json
├── package.json
├── webpack.main.config.js
├── webpack.renderer.config.js
├── webpack.rules.js
└── yarn.lock
```

The `.webpack` folder will contain the bundle application and will be rebuild each time you run `yarn start` and when building the app.

The `src` folder contains the css, html, js files and assets of your app *before* being handled by Webpack.

The `node_modules` folder contains all the packages you will install as well as all the packages your app needs to run properly.

The other files are basically config files and we will have to give a first look to them.

What I like is moving the configuration of electron-forge to its own partial file and calling it into my `package.json` file. It is clearer and cleaner.

In `package.json`, change the `config forge` name value to the following: 

``` JSON
// package.json

{
  ...
  "config": {
      "forge": "./forge.config.js"
  },
  ...
}
```

Then create a `forge.config.js` file in the root folder of your app and move the whole packager config, makers and plugins:

``` JS
// forge.config.js

const path = require('path');

module.exports = {
  packagerConfig: {},
  makers: [
    {
      name: "@electron-forge/maker-squirrel",
      config: {}
    },
    {
      name: "@electron-forge/maker-zip",
      platforms: [
        "darwin"
      ]
    },
    {
      name: "@electron-forge/maker-deb",
      config: {}
    },
    {
      name: "@electron-forge/maker-rpm",
      config: {}
    }
  ],
  plugins: [
    [
      "@electron-forge/plugin-webpack",
      {
        mainConfig: "./webpack.main.config.js",
        renderer: {
          config: "./webpack.renderer.config.js",
          entryPoints: [
            {
              html: "./src/index.html",
              js: "./src/renderer.js",
              name: "main_window"
            }
          ]
        }
      }
    ]
  ]
}

```
>Note that I changed the config from a `.json` file to a `.js` file. This is the reason why I updated the file and deleting the `"name"` quoting marks.
 
We will complete it later, depending on our makers and plugings. I'll explain all of that!

Electron-forge did everything for us so that we don't have to do it! 

1. `main.js` is where you put all the Electron app main process like the rendering of the window, the menus, etc.

2. `renderer.js` is where you gather and call all related styles, js, scss frameworks and libraries.

3. `webpack.main.config.js` for the main logic of webpack

4. `webpack.rules.js` is where we will declare our loaders which will process js files for example

5. `webpack.renderer.config.js` for our loaders which will process scss and assets files


Now, to keep it clean, we wil create specific folders for styles, javascript and images files.

Under the `src` folder, let's create a `scss`, `images` and `js` folders. 

We will move our `index.css` to the `scss` folder. For the moment, we do not change the file extension of our `index.css`, we'll do it a bit later.

Now, our tree looks like the following:

``` bash
📁 my-new-app
├── .git
├── .webpack
├── node_modules
├── src
│   ├── images
│   ├── js
│   └── scss
│       └── index.css
│   ├── index.html
│   ├── main.js
│   └── renderer.js
├── forge.config.js
├── package-lock.json
├── package.json
├── webpack.main.config.js
├── webpack.renderer.config.js
├── webpack.rules.js
└── yarn.lock
```

----

# 🌌 Installing and configuring loaders

Because your app will use different file extensions and language formats, we need loaders which will compile those files into readable JavaScript and CSS. 

When compiling, Webpack will first call the loaders and then bundle all our compiled files into a single `.js`, which is going to be added to our `index.html` automatically.

So, let's start! 🔥

## 1. Webpack

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kpqbxdiin5w9qtiucfea.png)

Because we used the electron-forge template for Webpack, the latter is already installed. However, if you didn't use this template, you need to install it. For the purpose of this tuto, I use `webpack v4`, which is the one installed by default by electron-forge at the moment:

```
yarn add --dev webpack@4.44.2
```

I use specifically this version so that it meets all my loaders required dependencies. I do not use Webpack new v5 because it still raises errors with the electron-forge template.


## 2. JS files

We need to tell webpack to handle all our `.js` files. And for that we will add loaders to our webpack.rules.js. 

We will use [Babel](https://babeljs.io/), which is probably the main JavaScript compiler to convert ECMAScript 2015+ code into a compatible version of JavaScript for all browsers or environments. 

Let's install the necessary loaders and preset. The setup guide is available on the [babel page](https://babeljs.io/setup).

Run the following command:

```
yarn add --dev @babel/core @babel/preset-env babel-loader
```

And then, we add the necessary configuration like so:

``` JS
// webpack.rules.js

...
{
    test: /\.m?js$/,
    exclude: /(node_modules)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env']
      }
    }
  },
...
```


## 3. scss files

We will do the same with style files.

It is important to handle scss files because Bootstrap uses Node Sass to compile Sass source files into CSS files.

So, as you may probably have understood, we will install sass and its loader. Here is the [GitHub's page](https://github.com/webpack-contrib/sass-loader) of the sass-loader will all necessary information! 

Let's install everything:

```
yarn add --dev sass sass-loader@10.1.1
```

Same as before, I use a specific version for the loader as the lastest version breaks with the electron-forge template we used.

And now we can call sass in our webpack config file:

``` JS
// webpack.renderer.config.js

const rules = require('./webpack.rules');

rules.push(
  {
    test: /\.(scss)$/, // all scss files will be handled
    // Use loaders in that specific reverse order
    use: [
      {
        loader: 'style-loader',
      },
      {
        loader: 'css-loader',
      },
      {
        loader: 'sass-loader'
      }
    ]
  },
);

module.exports = {
  // Put your normal webpack config below here
  module: {
    rules,
  },
};
...
```

## 4. Images

If you want to add images, you should follow the same process.
So first, we need the necessary loaders, and then, we configure Webpack.

As for the loaders, we need file-loader. The file-loader resolves import/require() on a file into a url and emits the file into the output directory.

Let's install it:

```
yarn add --dev file-loader
```

And then, we configure Webpack:

```JS
// webbpack.renderer.config.js

...
{
    test: /\.(png|jpe?g|gif|ico|svg)$/, // We will handle of these file extensions
    use: [
      {
        loader: "file-loader",
      }
    ]
  },
...
```

Now we will be able to call a file as a background image for example and Webpack will handle it to render it when you start your app. We will try it in a moment!

----

# 🗂️🗂️ Some organization again!

As you may have understand now, I like having my project folder clean. So I like organising my scss files as follows. It is also a good habit before adding bootstrap and other librairies.

Under the `scss` folder, we will rename our `index.css` to `app.scss`. As we installed the `sass-loader`, there is no problem here anymore! This file gonna be the entry point of all our scss files. So it will call our files and then, it is itself called by our `renderer.js`. So we need to tell Webpack about these changes!

``` JS
// renderer.js

console.log('👋 This message is being logged by "renderer.js", included via webpack');

import './scss/app.scss'; // just change this line here.
``` 

Then, from `app.scss`, we can call any custom file we may create! So, for example, let's create a `_style.scss` file in the `scss` folder and move the existing style which lives in `app.scss`.

>Note the `_` at the begining.

``` scss
// _style.scss

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  margin: auto;
  max-width: 38rem;
  padding: 2rem;
  background-color: pink; // yeah, I like to add some flashy stuff like that just to make it clear if this file has been correctly handled when I will start the app!
}
```
>Don't forget to delete the body style in `app.scss` then.

And now, we just have to call it in `app.scss`:

``` scss
// app.scss

@import "style"; // No _ here, type directly the name of the file
```

Now, if your run `yarn start`, everything should perfectly work with a beautiful pink background!

Remember that we also installed a `file-loader` for our images? What about trying it now?!

```scss
// _style.scss

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  margin: auto;
  max-width: 38rem;
  padding: 2rem;
  background-color: pink;

  // Add the line below and don't forget to add an image in your images folder!
  background: url('../images/electron-forge.png') no-repeat fixed center /120px;
}
```

Let's run `yarn start` and see the result! 👌

We're almost there! Now everything is ready and we can install Bootstrap and FontAwesome. 

You will see that a few more configuration is needed for that, but we did the most important (and complicated!) work already! 💪

----

# 💜 Installing Bootstrap

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d46a31csmbrbjavj26mq.png)

Whether you decide to install Bootstrap 5 or any other version, Bootstrap provides us with a [documentation](https://getbootstrap.com/docs/5.0/getting-started/webpack/) to help us achieve so. So do not hesitate to give an eye to it.

Let's add Bootstrap 5! ✨

First, we need to install Bootstrap in our **dependencies**, and not the devDependencies.

```
yarn add bootstrap@next
```

In order to work, Bootstrap needs additional librairies called `Popper`, [postcss and its loader](https://github.com/postcss/postcss) as well as the `autoprefixer` loader:

```
yarn add @popperjs/core && yarn add --dev autoprefixer postcss postcss-loader@4.2.0
```

As per the [Bootstrap documentation](https://getbootstrap.com/docs/5.0/getting-started/webpack/), the webpack `postcss-loader` needs `autoprefixer` we just installed. So let's add the necessary loader and options to our `webpack.renderer.config.js`:

``` js
// webpack.renderer.config.js

...
{
  loader: 'postcss-loader',
  options: {
    postcssOptions: {
      plugins: function () {
        return [
          require('autoprefixer')
        ];
      }
    }
  }
},
...
```

Then, import the Bootstrap JavaScript into your app by adding this line to your renderer.js:

```JS
// renderer.js 

import 'bootstrap';
import './scss/app.scss';
```

You also need to import the style files of Bootstrap. We will import it in our `app.scss`. Remember? I told you that this file will call all our styles:

``` scss
// app.scss

@import "~bootstrap/scss/bootstrap";
@import "style";
```

Lastly, let's integrate some Bootstrap components to our `index.html` file in order to see if everything works fine:

```HTML
// index.html

...
<div class="row">
  <div class="col-4">
    <div class="list-group" id="list-tab" role="tablist">
      <a class="list-group-item list-group-item-action active" id="list-electron-list" data-bs-toggle="list" href="#list-electron" role="tab" aria-controls="home">Electron</a>
      <a class="list-group-item list-group-item-action" id="list-app-list" data-bs-toggle="list" href="#list-app" role="tab" aria-controls="profile">my-sample-app</a>
      <a class="list-group-item list-group-item-action" id="list-aliceandbob-list" data-bs-toggle="list" href="#list-aliceandbob" role="tab" aria-controls="messages">aliceandbob.io</a>
      <a class="list-group-item list-group-item-action" id="list-github-list" data-bs-toggle="list" href="#list-github" role="tab" aria-controls="settings">My Github</a>
    </div>
  </div>
  <div class="col-8">
    <div class="tab-content" id="nav-tabContent">
      <div class="tab-pane fade show active" id="list-electron" role="tabpanel" aria-labelledby="list-electron-list">
        Check <a href="https://www.electronjs.org/" target="_blank">Electron website</a> to discover even more about all the possibilities! 🙀
      </div>
      <div class="tab-pane fade" id="list-app" role="tabpanel" aria-labelledby="list-app-list">
        Here you can find the <a href="https://github.com/theolazian/my-sample-app" target="_blank">link</a> to my-sample-app we just created thanks to the tutorial! 🤓
      </div>
      <div class="tab-pane fade" id="list-aliceandbob" role="tabpanel" aria-labelledby="list-aliceandbob-list">
        Wanna see a production ready application build with Electron? Go and check <a href="https://aliceandbob.io/" target="_blank">aliceandbob.io</a>! 🚀
      </div>
      <div class="tab-pane fade" id="list-github" role="tabpanel" aria-labelledby="list-github-list">
        and here is my <a href="https://github.com/theolazian" target="_blank">Github</a> page 🙏
      </div>
    </div>
  </div>
</div>
...
```

Note that if you want to change the default variable of Bootstrap, you need to call your custom variables *before* you import Bootstrap in our `app.scss`. Let's see an example!

First, create a `_custom_bootstrap.scss` file in our `scss` folder, and change some of Bootstrap variables:

``` scss
// _custom_bootstrap.scss

$theme-colors: (
  "primary": #e5e619,
  "secondary": #ff468b,
  "info": #00e3ff
);
```

And then, import it in our `app.scss` in order to add it to our bundle file:

``` scss
// app.scss

@import "custom_bootstrap"; // Remember, without the "_"
@import "~bootstrap/scss/bootstrap";
@import "style";
```

Now, run `yarn start` to see if everything works! 🎆🌟💫

----

# 🏁 Installing FontAwesome

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5c65t3kpzsmlmotbc95p.png)

As per the [FontAwesome documentation](https://fontawesome.com/how-to-use/on-the-web/setup/using-package-managers), you need to install it in the **dependencies** of your app. So, first, run the following command:

```
yarn add @fortawesome/fontawesome-free
```

Once it is installed, you need to add it to your `renderer.js` file to import it in your bundle file:

```JS
// renderer.js

...
import 'bootstrap';
import './scss/app.scss';
import "@fortawesome/fontawesome-free/js/all";
```

Now, you can add FontAwesome icons to your html file! Let's try it now:

```HTML
// index.html
...
<h1>💖 Hello World!</h1>
<p><i class="fas fa-atom"></i> Welcome to your Electron application.</p>
...
```

And now, `yarn start`, et voilà! 🎇👌

----

# 💲 Installing JQuery

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/69vkpwggnj16mmy2p7g9.png)

Let's say we want to use JQuery in our custom `js` files. We will first need to add JQuery and then create our custom file to use it.

In order to use JQuery, Webpack offers us a really simple solution. We just need to install JQuery in our dependencies and then use the Provide plugin of Webpack to easily being able to call JQuery everywhere in our app.

First, run this command to install JQuery:

```
yarn add jquery
```

And then, let's defined `webpack` in our `webpack.renderer.config.js` in order to use its ProvidePlugin and then to call it in the module.exports part:

```JS
// webpack.renderer.config.js

const webpack = require('webpack');
...

module.exports = {
  // Put your normal webpack config below here
  module: {
    rules,
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
};
```

Now that it's done, we will create a custom js file in our `js` folder and we will use a simple JQuery call:

```JS
// js/index.js

console.log("👋 This message is being logged by 'index.js', included via webpack"); // Just to make sure when I start the app that the file has been correctly handled by Webpack;

$( "body" ).addClass( "bg-info" ); // We now change the background-color thanks to Bootstrap classes and JQuery
```

And of course, we need to tell Webpack to include this new file in the bundle. So for that, we need to add `index.js` to `renderer.js`:

```JS
// renderer.js

...
import './js/index.js';
```

We now run `yarn start`, and everything works great! 🤯

----

# 🏗️ Packaging and building our app


We already did a lot!

So what about packaging our app and building it in order make it installable to Windows, MacOs or Linux systems?

Electron-forge provides us with a simple command to do so:

```
yarn make
```

Here, you can also choose, the platform and the architecture:

## Plateforms

You can pass the following params to the `make` command:

- Windows: `--platform=win32`
- MacOs: `--platform=darwin`
- Linux: `--platform=linux`

## Architectures

And depending on the architecture you want, you can pass either `--arch=ia32` or `--arch=x64`.

Other options are available. Look at the [electron-packager API](https://electron.github.io/electron-packager/master/interfaces/electronpackager.options.html#arch).

## A bit more configuration

⚠️ You can get some errors and warning here! It's because you cannot build everything from any platforms. Let's say you want to build a `.app` for MacOs, well, this can be done only from a MacOs system.. 

But no worries, you should be able to build plenty of apps! Just remember that depending on your machine, you may need to install packages and apps. 

You can also comment this post with your issue and people might come back to you with good pieces of advices! 🤝

>👁️ I also recommend you to go on the [makers page](https://www.electronforge.io/config/makers) of electron-forge to see the required tools.


----

I leave you here the link to this template, and also a link to a production ready app. Do not hesitate as there is a bit more configuration which can be useful for you, notably for the build process! 

You can go on [aliceandbob.io](https://aliceandbob.io) or check to Github page below:

{% github aliceandbob-io/aliceandbob-desktop no-readme %}

And if you want to fork the sample app we made all together, you can find it here:

{% github theolazian/electron-webpack-bootstrap-fontawesome-jquery no-readme %}

---- 

And here it is folks! I hope you didn't encountered (too many) issues! 😉 Otherwise, let me know in the comments below and I might update this tuto in accordance!

If you liked it, don't hesitate to leave a comment or a like! 🙏

Looking forward to seeing all your electron apps 😉

Thanks for reading and congrats for your work! 🏆🎉