First Electron app with React / Typescript

This is way easier than you think or at least way easier than I thought after seeing various boilerplate projects like electron-react-boilerplate.

To get started let’s create a new react app, a very regular react app, and on my preference — typescript.

npx create-react-app first-electron-app --template typescript

Assuming that all went well, the app is created and has been seen working using npm start, there are few more packages to install.

npm i -D electron electron-builder concurrently wait-on cross-env

The next step is to create electron startup file public/electron.js.

const { app, BrowserWindow } = require('electron');
const path = require("path");

const windowUrl = app.isPackaged
    ? `file://${path.join(__dirname, '../build/index.html')}`
    : `http://localhost:3000`;

let mainWindow;
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600
  });
  mainWindow.loadURL(windowUrl);
  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();
  }
});

I believe the code on public/electron.js is more or less self explanatory. The only thing to notice here is windowUrl — in case if application is packaged (by electron), it will pointing to a file, otherwise to http://localhost:3000 which is the default react app dev server endpoint. Also, the script is gonna be invoked by node so no typescript neither other fancy stuff here. Obviously it could be transpiled, but I don’t want to overcomplicate the example — the react part will be in typescript and that should be enough for now.

Few more adjustments to package.json and it will be ready to start.

"main": "public/electron.js",
"homepage": "./",
"scripts": {
  "start-dev-server": "cross-env BROWSER=none react-scripts start",
  "start-dev-app": "wait-on http://localhost:3000 && electron .",
  "start": "concurrently \"npm run start-dev-server\" \"npm run start-dev-app\"",
  "build": "react-scripts build && electron-builder",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}

Once it’s done we should be able to start the app in development mode by running npm start, or build a desktop app using npm run build.

Config explained

start-dev-server — starts a react development server. We use cross-env utility here to set a BROWSER=none environment variable which is a flag for react-scripts start not to start the browser.

start-dev-app — starts the electron app from current folder after dev server has started. The wait-on utility waits until the given endpoint (in our case http://localhost:3000 which is the react dev server default address) responds then exists. Once it exists we run electron . where dot is a path argument pointing to current directory.

Starting up electron will go for main script from package.json.

start — just runs dev server and app in parallel, and as mentioned above the app will start only after dev server has responded.

build — builds react app and then packs it using the electron-builder.

Since target directory is not specified (using --dir parameter) the electron-builder packs the ./build/, which in our case will be generated by react-scripts build. Unlike electron, electron-builder doesn’t look for main script in package.json, it expects electron.js to be in the root of target directory. Since it’s located in ./public/ directory it will get copied to ./build/ during react build process.

The homepage — very important, even tho the name in this context is totally misleading, The property is used by react-scripts build as base url for web resources like scripts, styles in etc, if not specified will default to /. As for example, without homepage specified, the script references in generated index.html will look like /app.js what would work in web, but electron will treat it as file system path and will look for app.js in the file system root, with homepage set to ./ the react-scripts would generate reference like ./app.js what electron will treat as relative to app directory and will be able to serve it.

BTW. Default output directory for electron build is ./dist/ folder, and it’s not a bad idea to add it to your .gitignore file.

A bit of branding

In package.json add build section, which, I believe, is self explanatory. The npm’s default author and description properties are recommended as well.

"author": {
  "name": "your name"
},
"description": "first electron app with react and typescript",
"build": {
  "appId": "com.example.first_electron_app",
  "productName": "Executable Name",
  "copyright": "Copyright © Year Author"
}

PS.

If something doesn’t work you can compare it with github version.

Obliviously this is not a complete production version guide, however I hope it helped you to get started and demonstrates how easy it is to pack a regular web app in electron and get a cross platform desktop application.

Links to more detailed docs

© 2020 Dennis Tretyakov