Building an Electron application with create-react-app

Electron is a popular open-source framework developed by GitHub that enables building cross-platform desktop applications with web technologies like JavaScript, HTML, and CSS. It combines the Chromium rendering engine and Node.js runtime into a single package, allowing developers to create rich native apps while leveraging their existing web development skills.

One of the benefits of Electron is that it provides access to platform-specific APIs and native operating system capabilities from a web-based application. This means you can create desktop apps that integrate with the file system, display native menus and dialogs, send system notifications, and more, all using familiar web technologies.

When it comes to building the user interface for an Electron app, many developers turn to React, a powerful JavaScript library for creating interactive UIs. React enables component-based UI development, efficient updates through its virtual DOM, and a large ecosystem of tools and libraries.

While it‘s possible to setup an Electron app with React manually, configuring webpack, Babel, and other build tools can be time-consuming and complex, especially for developers new to this stack. Fortunately, the create-react-app CLI tool makes it simple to bootstrap a React project with minimal configuration.

In this post, we‘ll walk through the process of building an Electron application using create-react-app. We‘ll cover the key steps to get up and running, discuss how to structure the Electron app code, and explore some best practices and common pitfalls when developing Electron apps with React.

Setting up the project

To get started, make sure you have Node.js installed on your development machine. Then, open a terminal and run the following command to generate a new React project using create-react-app:

npx create-react-app my-electron-app
cd my-electron-app

This will create a new directory called my-electron-app with the base files and configuration for a React application. Under the hood, it uses webpack and Babel to bundle and transpile the JavaScript code, along with other useful features like a development server with hot module replacement.

Next, install the Electron package from npm:

npm install --save-dev electron

Now let‘s add the main Electron process file. Create a new file called public/electron.js with the following contents:

const { app, BrowserWindow } = require(‘electron‘);

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  });

  win.loadURL(‘http://localhost:3000‘);
}

app.on(‘ready‘, createWindow);

This code creates a new BrowserWindow instance when the Electron app is ready, and loads the React app from the development server running at http://localhost:3000.

To run the Electron app, add a new script to package.json:

"electron": "electron ."

Your package.json should now include:

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject",
  "electron": "electron ."
},

You can now start the React development server and Electron app with:

npm start
npm run electron

This will open the Electron app window and load the React application. Any changes you make to the React code will automatically trigger a reload in Electron.

Electron app structure

A typical Electron app consists of two main process: the "main" process which runs the electron.js script, and one or more renderer processes that display the app‘s web-based UI.

The main process is responsible for lifecycle events like launching the app, creating windows, and handling system-level interactions. Each Electron window runs its own renderer process, which is essentially a Chromium instance that loads your web application code.

For our app, we‘ll keep the React code in the src directory, and add a public directory to contain the electron.js main process file, as well as any other assets like images or fonts that are not imported directly into the React components.

Here‘s an example directory structure:

my-electron-app/
  node_modules/
  public/
    electron.js
    index.html
  src/
    App.js
    index.js
  package.json

In the electron.js main process file, we can customize the appearance and behavior of the app window, handle events, and interact with the operating system. The renderer process, defined by the React code in src, is responsible for displaying the user interface and handling user input.

By default, the renderer process cannot directly access Node.js APIs due to security restrictions. However, Electron provides a way to selectively enable Node.js integration for a window by passing the nodeIntegration: true option when constructing the BrowserWindow, as shown in the previous code example.

Building for production

To package your Electron app for distribution, you‘ll first need to build the React application for production. Update the "build" script in package.json to include the --prod flag:

"build": "react-scripts build --prod",

Then run:

npm run build

This will generate a production-ready bundle of your React application in the build directory.

Next, install the electron-builder package, which provides a convenient way to package and distribute Electron apps:

npm install --save-dev electron-builder

Add a new "dist" script to package.json:

"dist": "npm run build && electron-builder --dir"

This script first runs the production build of the React app, then uses electron-builder to create a distributable package in the dist directory.

You‘ll also need to add some configuration for electron-builder in package.json:

"build": {
  "appId": "com.example.electron-react-app",
  "files": [
    "build/**/*",
    "node_modules/**/*"
  ],
  "directories": {
    "buildResources": "public"
  }
},

This specifies the files to include in the package, such as the build output and node_modules dependencies, and sets the resource directory to public, which contains assets for the Electron app.

To create the distribution package, run:

npm run dist

This will generate an executable file for your current platform in the dist directory, along with an installer package.

Accessing Electron and Node.js APIs

One of the key benefits of Electron is the ability to access native system capabilities and Node.js modules from your web-based application code. However, there are some important considerations to keep in mind.

In the renderer process, you can use the electron module to access Electron-specific APIs, such as ipcRenderer for inter-process communication, or remote for invoking main process modules from the renderer.

To use the electron module in a React component, you can import it like this:

import { ipcRenderer } from ‘electron‘;

However, when using create-react-app, you may encounter an error saying that the module cannot be found. This is because the electron module is not bundled with the web application code.

One workaround is to use the window.require function to load the module at runtime:

const { ipcRenderer } = window.require(‘electron‘);

This will work because Electron‘s BrowserWindow sets the require function on the window object when nodeIntegration is enabled.

Similarly, to use Node.js modules in the renderer process, you can use window.require:

const fs = window.require(‘fs‘);

Just be aware that using Node.js modules directly in renderer code can introduce security risks if not handled properly, so exercise caution and validate any user input or remote content.

Debugging and testing

Debugging an Electron app with create-react-app is relatively straightforward. In the BrowserWindow options, set the webPreferences.devTools property to true:

const win = new BrowserWindow({
  width: 800,
  height: 600,
  webPreferences: {
    nodeIntegration: true,
    devTools: true
  }
});

This will open the Chrome Developer Tools when the app window loads, allowing you to inspect the DOM, console output, network requests, and more. You can also use the React Developer Tools extension to debug React components and state.

For automated testing, you can use the same testing frameworks and utilities that you would use for a regular React application, such as Jest for unit tests and React Testing Library for component tests. Just make sure to mock any Electron or Node.js specific modules that you use.

Alternative approaches

While using create-react-app is a convenient way to bootstrap an Electron app with React, there are other approaches you can consider depending on your needs:

  • Manually configuring webpack: If you have more advanced build requirements or need fine-grained control over the webpack configuration, you can setup the build pipeline manually. This involves installing and configuring webpack, Babel, and other necessary libraries, and writing your own webpack.config.js file.

  • Using a boilerplate project: There are several boilerplate projects and templates available for building Electron apps with React, such as electron-react-boilerplate and electron-webpack. These provide pre-configured setups with additional features like hot module replacement, CSS support, and packaging scripts.

  • Trying a different framework: While React is a popular choice for building Electron app UIs, you can also consider other frameworks like Angular, Vue.js, or Svelte. Each has its own ecosystem and tooling, so you‘ll need to evaluate which one best fits your skills and requirements.

Conclusion

In this post, we explored how to build an Electron application using create-react-app. We walked through the steps to setup the project, discussed the Electron app structure and main/renderer processes, and covered how to package the app for distribution.

We also looked at some of the key considerations when developing Electron apps with React, such as accessing Electron and Node.js APIs from renderer code, debugging and testing strategies, and alternative approaches to configuring the build pipeline.

While there is much more to learn about Electron app development, using create-react-app provides a solid foundation to get started quickly. With the power of Electron and the flexibility of React, you can build cross-platform desktop apps that deliver native experiences while leveraging the full potential of web technologies.

Similar Posts