Start a new Electron app with React and Typescript.

What is Electron?

Electron is a framework for creating native applications. It's open-source and cross-platform. If you already know Javascript, HTML, and CSS you can build an application with electron.

In this tutorial, I'll show you how to start an electron project from scratch using webpack, react, and Typescript.

Start with Electron.

Let's start by creating a new folder and a new npm project.

mkdir electron-react-ts
cd electron-react-ts
npm init -y

Now install these dependencies.

npm install --save-dev electron \
webpack webpack-cli webpack-dev-server \
babel-loader @babel/core @babel/preset-env \
@babel/preset-react @babel/preset-typescript

Create a tsconfig.json file. This allows you to specify the configuration for the typescript compiler.

{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": [
"dom",
"es2015",
"es2016",
"es2017"
],
"allowJs": true,
"jsx": "react",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
}
}

Create a babel.config.js and an index.html file at the root of our app.

module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript'
]
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Electron App</title>
</head>
<body>
</body>
</html>

Let's create a new file called webpack.electron.config.js on the root of our app. This webpack file will compile our electron app into a dist folder.

const path = require('path');
module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
devtool: 'source-map',
entry: './electron/main.ts',
target: 'electron-main',
module: {
rules: [
{
test: /\.(js|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].js',
},
};

This looks like a normal webpack configuration for typescript, except for the target. The target is the specific environment that webpack will compile for. In this case it's electron-main.

Create an electron folder, then inside a main.ts file with the following code. This file should create windows and handle the systems events for your app.

import { app, BrowserWindow } from 'electron';
import * as path from 'path';
import * as url from 'url';
let mainWindow: Electron.BrowserWindow | null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
},
});
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL(`http://localhost:4000`);
} else {
mainWindow.loadURL(
url.format({
pathname: path.join(__dirname, '../index.html'),
protocol: 'file:',
slashes: true
})
);
}
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.on('ready', createWindow);
app.allowRendererProcessReuse = true;

The BrowserWindow module will create a new window and render our react app.

Now let's add a script in the package.json file in order to run electron. Also, we have to change the main field for the path that has our electron app compiled.

{
"main": "./dist/main.js",
"scripts": {
"dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron ."
},
}

Now run npm run dev:electron in the console.

Note: If you are using Windows, chances are you'll face an error, this is because NODE_ENV is not recognized as a command. You have to install crossenv and place the command before NODE_ENV.

Add a React app.

Now that we have our electron app running, let's set up a react app to run within this electron context.

We need to install a few dependencies.

npm install react react-dom @types/react @types/react-dom
npm install --save-dev html-webpack-plugin

Create a new webpack.react.config.js file.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.js'],
mainFields: ['main', 'module', 'browser'],
},
entry: './src/app.tsx',
target: 'electron-renderer',
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
devServer: {
contentBase: path.join(__dirname, '../dist/renderer'),
historyApiFallback: true,
compress: true,
hot: true,
port: 4000,
publicPath: '/',
},
output: {
path: path.resolve(__dirname, '../dist/renderer'),
filename: 'js/[name].js',
publicPath: './',
},
plugins: [
new HtmlWebpackPlugin(),
],
};

Our package.json file now should look like this.

{
"main": "./dist/main.js",
"scripts": {
"dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .",
"dev:react": "NODE_ENV=development webpack-dev-server --config webpack.react.config.js --mode development"
},
}

In order to try this app, let's create a new folder src with anapp.tsx file inside.

import React from 'react';
import ReactDom from 'react-dom';
const mainElement = document.createElement('div');
document.body.appendChild(mainElement);
const App = () => {
return (
<h1>
Hi from a react app
</h1>
)
}
ReactDom.render(<App />, mainElement);

Now we are ready. Run npm run dev:react in one console, and npm run dev: electron on other one.

Electron app

Check this repo for the code.