React typescript & Electron — the one minute setup

This is the real try-force, no ?

The purpose of this story is to make a basic ready to use Electron app with React typescript.

🌀 What is Electron ?

Electron is an open-source framework for desktop GUI applications development using web technologies. It combines the Chromium rendering engine and the Node.js runtime.
The repository is here and the website here.

⚛️ What is React ?

React is an open-source, front end, JavaScript library for building user interfaces or UI components.
The repository is here and the website here.

🔘 What is Typescript ?

TypeScript is a programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript and adds optional static typing to the language.
The repository is here and website here.

📦 What is Parcel ?

Parcel is a compiler for the code, regardless of the language or toolchain.
The repository is here and website here.

🚀 Let’s go for the setup !

From here, I consider you’ve already installed node and yarn or npm. If it’s not the case take the time to take in hand the installation of it via the node documentation and the yarn documentation.

💡i work with yarn but all the process is functional with npm too.

1° step — Init the project

First things first we need to create a project folder :

// create the folder
mkdir electron-app
// go into it
cd electron-app

After this step we need to init yarn and git :

git init
yarn init

💡 it’s up to you to create the other file which could be useful like a .gitignore for example

Ok the project is initialized, so, no more blabla, let’s go !

2° step — Install deps

Now we need to install all packages needed by our project !

The installation of electron is really simple, it just needs a simple node package :

yarn add -D electron 

💡 like the official documentation said, you need to install electron in the develop dependencies.

The installation of react is really simple too, for now we just need two packages (later you can add react-router if needed for example) :

yarn add react react-dom

Because we need to work with typescript we need to add “types” packages for both of them :

yarn add -D @types/react @types/react-dom 

To continue with the complicated installations we now add typescript :

yarn add -D typescript

And parcel (Parcel will be used to generate the build of react) :

yarn add -D parcel

Ok here we have all packages we needed !

3° step — setup typescript

This is not very complicated, we just need to have a tsconfig.json in our project root :

💡 no magic here we have just copy/paste the tsconfig.json taken from the react repo, i just updated some options like the es target.

// tsconfig.json{
"compilerOptions": {
"module": "esnext",
"strict": true,
"experimentalDecorators": true,
"sourceMap": true,
"outDir": "./dist",
"baseUrl": ".",
"target": "ES2020",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src/**/*"
]
}

And voila ! You have a typescript ready setup 🙃

4° step — setup react

After that we need to have the minimum for a functional react project :

  • an index.tsx file for the injection of the script
  • an index.html file for the dom
  • and an app.tsx file for a basic react component

The index.html has to be located in a public folder, and the two other files in the src folder. Now we have the following tree :

├── public 
│ └── index.html
├── src
│ ├── app.tsx
│ └── index.tsx
├── node_modules
├── tsconfig.json
├── package.json
└── yarn.lock

In the index.html we put the following code, nothing special here this is the classic html file from react:

// public/index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>🚀 React Electron App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

In the index.tsx we need to have the react DOM render function :

// src/index.tsximport React from 'react';
import ReactDOM from 'react-dom';
import App from './app';

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

And finally the app.tsx, here you put everything you want, for example :

// src/app.tsximport React from 'react';

const App = (): JSX.Element => (
<div>
<p>😶‍🌫️ Hello there</p>
<p>🤖 General Kenobi !</p>
</div>
);

export default App;

Ok now we have a react with typescript setup, let’s merge all this together in electron !

5° step — setup electron

In the 2° step we have installed the electron package, now we need to initialize the main script of electron.
First thing first you need to create a file named electron.js in your src folder.

💡 The name of the file and its position into the project is fully personal, in theory you can name and place the file wherever you want.

// src/electron.jsconst { app, BrowserWindow } = require('electron');
const path = require('path');

/**
* creates a new browser window with a preload script and loads index.html file into this window
* @returns {void}
*/
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
height: 600,
width: 800,
});

// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));

// Open the DevTools
mainWindow.webContents.openDevTools();
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
createWindow();

app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

The purpose of this script is quite simple, I commented out all the lines of the script so you can understand it.

And just add some security stuff in our index.html :
if you want to understand why take a look here

// public/index.html<head>
<meta http-equiv="Content-Security-Policy" content="script-src 'self';" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self';" />
</head>

In our script we have set the following line :

mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));

This function load the index.html stored into the dist folder. Ok but we don’t have a dist folder 🤔

Yeah it’s normal because this is the build folder from parcel !

6° step — build react w/ parcel

💡 In theory electron can take an url as content to inject into the generated window. We can therefore do without parcel, run react with react-script and inject localhost:3000 into electron. But personally I prefer to have only build files to inject because I find it closer to the production configuration.

For the configuration of parcel we need to edit our package.json :

// package.json"scripts": {
"start": "parcel serve ./public/index.html --public-url ./"
}

and our index.html :

// public/index.html<body>
<script src="../src/index.tsx"></script>
</body>

💡 in our start script the public-url flag is important for electron, if its not set to ./ electron will not find the index.tsx injected to our index.html

Ok now we have a launch script for parcel and the minimum setup recommended by parcel for a react project (see doc here).

Here, if you launch the script, parcel will create a build folder call dist and will launch a server in localhost:1234. Because the script is set to serve if you edit a file into react parcel will relaunch the build automatically.

⚠️ Because we have set the public-url to ./ the parcel server (localhost:1234) will not show the react app. If you want to do it just remove the flag into your script.

Ok now we have a fully functional react let’s go to inject it into electron ! 🚀

6° step— inject react into electron

In reality we have done all the jobs into the 5° step 😇

Here we just need to add the script for launching electron to our package.json :

// package.json"main": "./src/electron.js",
"scripts": {
// other script
"electron": "electron ."
},

Here we have :

  • set the main tag to indicate electron which file is the input file
  • and the electron script for launching this file

7° and final step — launch the ship

If you run :

yarn start

and, after, :

yarn electron

You have your react app into your electron !

Congrat’s folk’s, you have react/ts in your electron app 🎉

To go further we can add :

  • a hot reload module to electron for avoiding the not practical ctrl+r for reload with electron-reloader
  • a single start command for launching parcel and electron at the same time with concurrently
  • automatically close the dev console when we are in production with electron-is-dev

🔗 Electron React hot reload & multi launch script — the one minute setup

lead & dev @helyx