How to Bundle your React-Electron App by Parcel

How to Bundle your React-Electron App by Parcel

Building a boilerplate for React + Electron + Parcel with hot reload
Ferenc Almasi β€’ Last updated 2021 August 02 β€’ Read time 8 min read
Learn how you can create a boilerplate for Electron that uses React and is built by Parcel with hot reload.
  • twitter
  • facebook
React

Recently, I wanted to build a desktop app using Electron as the framework, and React as the UI library. On top of that, I wanted a no-configuration setup, so I’ve turned to Parcel. I looked into a couple of tutorials, but either they didn’t mention hot reload, or the solutions required to start multiple separate processes. To simplify things, I’ve created a boilerplate that others can reuse as well, and so this tutorial was born to explain how it works.

Clone the boilerplate from GitHub


Setting Up Parcel

First, create a new npm project by running npm init -y, then install these two dependencies that we are going to work with:

npm i electron parcel-bundler

We will need a minimal setup, so add a public folder with an index.html file:

Copied to clipboard! Playground
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="Content-Security-Policy" content="script-src 'self';" />

        <title>πŸ“¦πŸš€ React Electron Parcel Boilerplate</title>
        <link rel="stylesheet" href="./assets/reset.css" />
    </head>
    <body>
        <div id="app"></div>
        <script src="../src/index.js"></script>
    </body>
</html>
index.html

There are two important things to note. First, you have a Content-Security-Policy meta tag. This is needed if you want to get rid of the security warning Electron throws in the console.

Electron security warning

Secondly, I’ve referenced a script called index.js under the src folder, so make sure you create that. An empty file will be enough for now. To let Parcel build these files, create a scripts folder and add a start.js file with the following content:

Copied to clipboard! Playground
const Bundler = require('parcel-bundler');

const entry = './public/index.html';
const options = {
    outDir: './build',
    publicUrl: './',
    sourceMaps: false,
    autoInstall: false,
    hmr: false,
    target: 'electron'
};

(async () => {
    const bundler = new Bundler(entry, options);

    bundler.bundle();
})();
start.js
Make sure you set the target to electron

This will create a new Parcel bundler, with a couple of config options. It sets the build directory to build. By default, this is set to dist. It forces assets to be referenced relative to your generated index file, this is why publicUrl is set. It also forces the bundler to avoid generating source maps and auto-installing missing dependencies. You also want to set hmr to false, otherwise, you may run into errors saying:

Uncaught DOMException: Failed to construct 'WebSocket': The URL 'ws://:58083/' is invalid.

If you want to further fine-tune your options, you can reference the API docs on the official site of Parcel. To make this script easier to start, add a start command to your package.json file:

Copied to clipboard!
"scripts": {
    "start": "node scripts/start.js"
},
package.json

If you did everything right, you should see a generated build folder, once you run npm run start.


Adding Electron

To add Electron to the project, add a new script to your package.json that we can use to start electron:

Copied to clipboard!
"scripts": {
    "start": "node scripts/start.js",
    "electron": "electron ."
}
package.json

When starting Electron, it looks for the main file specified in your package.json. To keep the project’s root clean, add an electron folder into your src directory, and create an index.js, then make sure you reference this in your package.json:

Copied to clipboard!
"main": "src/electron/index.js"
package.json

In your index.js for Electron, reference two new files; one for production, and one for development builds.

Copied to clipboard!
process.env.NODE_ENV === 'production'
    ? require('./prod.js')
    : require('./dev.js');
index.js

For a basic setup, you can add the following content to your files. Enable nodeIntegration to avoid Parcel running into errors:

Copied to clipboard! Playground
const { app, BrowserWindow } = require('electron');

const createWindow = () => {
    const window = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });

    window.loadFile('build/index.html');
};

app.whenReady().then(createWindow);
app.on('window-all-closed', () => app.quit());
dev.js

Make sure you load the generated index.html file from the build folder. To also start Electron with npm run start, you want to modify your start.js script a bit. Under the options, add the following:

Copied to clipboard! Playground
let electronStarted = false;

(async () => {
    const bundler = new Bundler(entry, options);

    bundler.on('bundled', bundle => {
        if (!electronStarted) {
            electronStarted = true;

            require('child_process').spawn('npm', ['run', 'electron'], {
                stdio: ['ignore', 'inherit', 'inherit'],
                shell: true
            });
        }
    });

    bundler.bundle();
})();
start.js

This will spawn a new process, once Parcel bundled the assets together for the first time. Run npm run start again, and you should see Parcel firing up Electron.

Electron window started with Parcel
If you have issues opening the DevTools, try running your command in non-admin mode. See #20069
Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScriptinfo Remove ads

Adding Hot Reload

If you try to modify an asset, it won’t do anything at the moment, so let’s add hot reload. We will use the electron-reloader module from Sindre Sorhus. Run npm i electron-reloader to install it, then all you have to do is add the following to the beginning of your dev.js file, before you create the window for Electron:

Copied to clipboard!
try {
    require('electron-reloader')(module);
} catch (_) {}
dev.js

Try to change any of your assets, and Electron should be reloaded automatically.

Electron with Parcel hot reloaded

Adding React to the Project

To add React to this project, simply install the necessary dependencies:

npm i react react-dom

Bootstrap your app in your src/index.js file:

Copied to clipboard! Playground
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'

ReactDOM.render(<App />, document.getElementById('app'));
index.js

And you are all done. The App component exports a slightly modified version of Create React App, which you can check out in the GitHub Repository.

undefined

Building the Project

Lastly, you want to build the project for the end-users. For this, we will need a couple of more packages. Namely:

npm i electron-packager electron-winstaller
  • electron-packager is responsible for creating an executable file for the project, but this won’t create a single installer.
  • electron-winstaller can be used to create an installer from the package that electron-packager generates.

Add a build script to your package.json and a build.js to your scripts folder as well:

Copied to clipboard!
"scripts": {
    "build": "node scripts/build.js"
}
package.json

To build the executable and the installer, add the following to your build.js script:

Copied to clipboard! Playground
const packager = require('electron-packager');
const electronInstaller = require('electron-winstaller');

async function build(options) {
    const appPaths = await packager(options);

    console.log(`βœ… App build ready in: ${appPaths.join('\n')}, creating installer...`);

    try {
        await electronInstaller.createWindowsInstaller({
            appDirectory: './dist/app-win32-ia32',
            outputDirectory: './dist/installer',
            authors: 'Weekly Webtips',
            description: 'πŸ“¦πŸš€ Electron app using React, built with Parcel',
            exe: 'app.exe'
        });

        console.log('πŸ’» Installer is created in dist/installer');
    } catch (e) {
        console.log(`The following error occured: ${e.message}`);
    }
};

build({
    name: 'app',
    dir: './',
    out: 'dist',
    overwrite: true,
    asar: true,
    platform: 'win32',
    arch: 'ia32'
});
build.js

For the list of available options for electron-packager, you can refer to their official documentation. The electron-winstaller package also has a supported settings table you can check out. A couple of things to keep in mind to avoid getting errors while the installer is being generated:

  • The installer package uses your name field from your package.json. Make sure this doesn’t contain any dashes or special characters.
  • Also make sure you reference the right directory and file in createWindowsInstaller, the one that is generated by packager.

Using the script above, it will generate an executable first in the dist folder, and based on the generated files, it will also create an installer.


Summary

And now you have the base for an Electron application with React, built by Parcel β€” the zero-configuration web app bundler. Although this setup is configurable to some extent using its bundler API. As mentioned in the beginning, you can clone the whole project from the following GitHub repository:

Clone the boilerplate from GitHub

Do you have anything else to add to this tutorial? Let us know in the comments below! Thank you for reading through, happy coding!

How to Make Your Very First Desktop App With Electron and Svelte
  • twitter
  • facebook
React
Did you find this page helpful?
πŸ“š More Webtips
Mentoring

Rocket Launch Your Career

Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies:

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.