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.
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:
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
andjs
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
🌱 Step by Step initialization
Lets's go step by step together through the process!
1️. Electron-forge
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
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:
- Head to your project directory from the PowerShell command line tool
cd path/to/your/repo
,- Run
npm install --save-dev electron
,- 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:
// 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!
main.js
is where you put all the Electron app main process like the rendering of the window, the menus, etc.renderer.js
is where you gather and call all related styles, js, scss frameworks and libraries.webpack.main.config.js
for the main logic of webpackwebpack.rules.js
is where we will declare our loaders which will process js files for examplewebpack.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
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, 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
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 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:
// 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?!
// _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
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
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
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
:
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:
// 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:
// 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
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
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";
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>
...
And now, yarn start
, et voilà! 🎇👌
💲 Installing JQuery
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:
// 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/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
:
// 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.
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:
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:
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! 🏆🎉
Top comments (6)
Hi, I believe there's a small typo in the code for importing postcss, as @remzi has pointed out. The code specified is
This throws the error:
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: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!
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:
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 ofwebpack.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!
I'm very sorry, but it seems like your tutorial isn't working anymore! Please help!
Sorry for this late answer! What's the issue? I'll do my best to help ☺️
how can i add axios to this project?
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....