DEV Community

Cover image for Electron app with Webpack, Bootstrap, FontAwesome and JQuery - A complete guide
Théo
Théo

Posted on • Updated on

Electron app with Webpack, Bootstrap, FontAwesome and JQuery - A complete guide

tl;dr

A step-by-step tutorial to build from scratch an electron app together with Bootstrap, FontAwesome, JQuery and bundled with Webpack! 🎉


⚓ Introduction

As I was building an electron app for a project, 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 or head to the GitHub's page of the project.

GitHub logo aliceandbob-io / aliceandbob-desktop

🔐 A free, light and easy to use desktop tool to generate PGP key pairs, encrypt and decrypt messages.

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

GitHub logo theolazian / electron-webpack-bootstrap-fontawesome-jquery

🤓💻 A sample electron app built with Webpack, Bootstrap, FontAwesome and JQuery

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!

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 of Node and install the version you need.

Then, install yarn as follows:

npm install --global yarn
Enter fullscreen mode Exit fullscreen mode

🌱 Step by Step initialization

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

1️. Electron-forge

Alt Text

Electron-forge 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

or

yarn start
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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":

// package.json

{
  ...
  "main": "./.webpack/main",
  ...
}
Enter fullscreen mode Exit fullscreen mode

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:

// main.js

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

Now we're free to move on! 🐌


🗂️ Structuring the app

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

📁 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
Enter fullscreen mode Exit fullscreen mode

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:

// package.json

{
  ...
  "config": {
      "forge": "./forge.config.js"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

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

// 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"
            }
          ]
        }
      }
    ]
  ]
}

Enter fullscreen mode Exit fullscreen mode

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:

📁 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
Enter fullscreen mode Exit fullscreen mode

🌌 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

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
Enter fullscreen mode Exit fullscreen mode

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, 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.

Run the following command:

yarn add --dev @babel/core @babel/preset-env babel-loader
Enter fullscreen mode Exit fullscreen mode

And then, we add the necessary configuration like so:

// webpack.rules.js

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

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 of the sass-loader will all necessary information!

Let's install everything:

yarn add --dev sass sass-loader@10.1.1
Enter fullscreen mode Exit fullscreen mode

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:

// 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,
  },
};
...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And then, we configure Webpack:

// webbpack.renderer.config.js

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

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!

// renderer.js

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

import './scss/app.scss'; // just change this line here.
Enter fullscreen mode Exit fullscreen mode

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.

// _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!
}
Enter fullscreen mode Exit fullscreen mode

Don't forget to delete the body style in app.scss then.

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

// app.scss

@import "style"; // No _ here, type directly the name of the file
Enter fullscreen mode Exit fullscreen mode

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?!

// _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;
}
Enter fullscreen mode Exit fullscreen mode

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

Whether you decide to install Bootstrap 5 or any other version, Bootstrap provides us with a documentation 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
Enter fullscreen mode Exit fullscreen mode

In order to work, Bootstrap needs additional librairies called Popper, postcss and its loader as well as the autoprefixer loader:

yarn add @popperjs/core && yarn add --dev autoprefixer postcss postcss-loader@4.2.0
Enter fullscreen mode Exit fullscreen mode

As per the Bootstrap documentation, the webpack postcss-loader needs autoprefixer we just installed. So let's add the necessary loader and options to our webpack.renderer.config.js:

// webpack.renderer.config.js

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

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

// renderer.js 

import 'bootstrap';
import './scss/app.scss';
Enter fullscreen mode Exit fullscreen mode

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:

// app.scss

@import "~bootstrap/scss/bootstrap";
@import "style";
Enter fullscreen mode Exit fullscreen mode

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

// 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>
...
Enter fullscreen mode Exit fullscreen mode

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:

// _custom_bootstrap.scss

$theme-colors: (
  "primary": #e5e619,
  "secondary": #ff468b,
  "info": #00e3ff
);
Enter fullscreen mode Exit fullscreen mode

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

// app.scss

@import "custom_bootstrap"; // Remember, without the "_"
@import "~bootstrap/scss/bootstrap";
@import "style";
Enter fullscreen mode Exit fullscreen mode

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


🏁 Installing FontAwesome

Alt Text

As per the FontAwesome documentation, you need to install it in the dependencies of your app. So, first, run the following command:

yarn add @fortawesome/fontawesome-free
Enter fullscreen mode Exit fullscreen mode

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

// renderer.js

...
import 'bootstrap';
import './scss/app.scss';
import "@fortawesome/fontawesome-free/js/all";
Enter fullscreen mode Exit fullscreen mode

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

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

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


💲 Installing JQuery

Alt Text

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
Enter fullscreen mode Exit fullscreen mode

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:

// 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'
    })
  ]
};
Enter fullscreen mode Exit fullscreen mode

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/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
Enter fullscreen mode Exit fullscreen mode

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:

// renderer.js

...
import './js/index.js';
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

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 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 or check to Github page below:

GitHub logo aliceandbob-io / aliceandbob-desktop

🔐 A free, light and easy to use desktop tool to generate PGP key pairs, encrypt and decrypt messages.

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

GitHub logo theolazian / electron-webpack-bootstrap-fontawesome-jquery

🤓💻 A sample electron app built with Webpack, Bootstrap, FontAwesome and JQuery


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! 🏆🎉

Oldest comments (6)

Collapse
 
msuagonz profile image
msuagonz

how can i add axios to this project?

Collapse
 
msuagonz profile image
msuagonz

Ok, i do this and work

first
npm install axios --save

then
npm install --save @babel /polyfill

and, in the file renderer.js y add this 2 lines

import "core-js/stable";
import "regenerator-runtime/runtime";

work for me....

Collapse
 
remzi profile image
Remzi Cavdar

I'm very sorry, but it seems like your tutorial isn't working anymore! Please help!

Collapse
 
theola profile image
Théo

Sorry for this late answer! What's the issue? I'll do my best to help ☺️

Collapse
 
arnyminerz profile image
Arnyminer Z • Edited

Hi, I believe there's a small typo in the code for importing postcss, as @remzi has pointed out. The code specified is

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

This throws the error:

An unhandled rejection has occurred inside Forge:
CssSyntaxError: C:\Users\...\src\index.html:1:1: Unknown word

> 1 | <!DOCTYPE html>
    | ^
  2 | <html>
  3 |   <head>


Electron Forge was terminated. Location:
{}
Enter fullscreen mode Exit fullscreen mode

I thought this had something to do with the css handler taking html files, so I simply added a test parameter to the postcss loader field. Afterall, the postcss part in webpack.renderer.config.js is:

...
{
    test: /\.(css)$/,
    loader: 'postcss-loader',
    options: {
        postcssOptions: {
            plugins: function () {
                return [
                    require('autoprefixer')
                ];
            }
        }
    }
},
...
Enter fullscreen mode Exit fullscreen mode

I'm not very experienced neither with Electron nor Webpack, but I believe this is a valid fix. Correct me if I'm wrong ;).
Thanks for the great tutorial, by the way, really helpful!

Collapse
 
theola profile image
Théo • Edited

Thanks a lot for your message and your words! :)

And thank you for the proposed fix! I re-read the post, and actually you will see that above in the post, under the section Installing and configuring loaders > 3. scss files, I have already written the test:

// 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,
  },
};
...
Enter fullscreen mode Exit fullscreen mode

However, the snippet code that you quoted is only the relevant section regarding the adaptation of the postcss-loader: for the sake of readability, I didn't rewrite the whole code of webpack.renderer.config.js there. But you can find it here if needed.

So I think that everything's fine! :) But please let me know if I missed something or if you have any other issue!