Package a React application with Electron in 4 minutes

Sergi Jiménez
4 min readSep 1, 2024

--

React and Electron banner
Author owns image rights. Icons from app developers.

Over time React has established itself as one of the most popular libraries for creating user interfaces. However, as web applications become more complex, the need arises to take these experiences beyond the browser. This is where Electron comes in. With it, you can package your React application and distribute it as a desktop application for Windows, macOS or Linux without needing to rewrite the code base.
In this article, I’ll walk you step-by-step through the process of packaging a React application using the tools provided by Electron. Let’s get to it!

What is React and what is Electron?

React is a JavaScript library used to build interactive and dynamic user interfaces. Its main feature is the ability to create reusable components, which facilitates the development and organization of complex applications.
Electron is a framework that allows developers to transform web applications into cross-platform desktop applications, using web technologies such as HTML, CSS and JavaScript.

Before we begin…

Before starting it is important to have NodeJS installed in our system. We will be able to install it through its web page. In addition, this project uses Vite.

How do we package our application?

First we will have to locate inside our project by means of a terminal, since we will install Electron. To install it we will type the following:

npm i -D electron electron-is-dev
npm install electron electron-builder --save-dev

As we can see we not only install Electron, but we also install a library called electron-is-dev in charge of checking if our application is running in development or production. Once both dependencies are installed we will proceed to configure them.

To configure Electron inside our React application we will have to create a file which we will call electron.js in the root of our application. Inside our electron.js file we will write the following:

import { app, BrowserWindow } from 'electron';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import isDev from 'electron-is-dev';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1080,
height: 920,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webSecurity: true,
},
});
let startURL;
if (isDev) {
startURL = '<http://localhost:5173>';
} else {
startURL = `file://${join(__dirname, 'dist', 'index.html')}`;
}
mainWindow.loadURL(startURL);
mainWindow.on('closed', () => (mainWindow = null));
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});

This code creates a desktop application with Electron that opens a main window loading your React application. It also handles key application lifecycle events, such as creating windows, closing the application, and activating the main window on macOS.

Finally we will change our package.json to look like this:

...
"scripts": {
"preview": "vite preview",
"react": "vite",
"electron": "electron .",
"build": "tsc -b && vite build",
"dist": "electron-builder"
},
"main": "electron.js",
"build": {
"appId": "com.example.your_app",
"productName": "your_app",
"directories": {
"output": "dist_electron"
},
"files": [
"dist/**/*",
"electron.js"
],
"extraResources": {
"from": "dist/",
"to": "app/dist/"
},
"mac": {
"target": "dmg",
"category": "public.app-category.productivity"
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"icon": "build/icon.ico"
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true
},
"linux": {
"target": [
"AppImage",
"deb",
"tar.gz"
],
"category": "Utility",
"icon": "build/icons"
}
}
}

We can see that we have separated the way in which we execute our application, in this case changing the start command for react, and we have added a script to execute Electron. Then, we have implemented some settings for building and packaging the application.

We will adapt the vite.config.ts file adding the base property so that the application can locate the Javascript and CSS files in the index.html, generated in the dist folder when executing the npm run build command, making the paths relative instead of absolute. This is how the file should look like:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// <https://vitejs.dev/config/>
export default defineConfig({
base: './',
plugins: [react()],
})

To check that everything works correctly before packaging our application we will open two terminals typing some commands in a specific order. First, in one of them we will type the following command:

npm run react

And in the other terminal type the following command:

npm run electron

This way our application will open as if it were a desktop application.

If at some point you have a problem with electron-is-dev when running the application, choose to install it as a dependency instead of as a development dependency. Once verified that everything works correctly we will cancel all the processes in execution to be able to make the packaging.

To package our application we will have to execute two commands in the indicated order. First we will type in our terminal the following command:

npm run build

Finally, we can have our application packaged and ready to run with the following command:

npm run dist

And that’s it! Once the process is completed we can go into the newly created directory, locate the executable or the installer, and we will be able to check that the application works perfectly.

Conclusion

Packaging a React application with Electron is a simple process that allows you to turn web applications into desktop applications in just a few steps. This capability gives developers a powerful tool to extend the reach of their applications by making them easier to distribute — now you’re ready to take your applications to the next level!

--

--

Sergi Jiménez
Sergi Jiménez

Written by Sergi Jiménez

Con una taza de café en una mano y un teclado en la otra, ¡soy el maestro del código y el rey de los bugs! https://github.com/sergiJimenez

No responses yet