Setting Electron + React with Typescript
Tags: react, tutorial, electron, typescript
Published: 2020-08-06 14:52:04


---
title: Setting Electron + React with Typescript
published: true
cover_image: https://res.cloudinary.com/practicaldev/image/fetch/s--HII-pSoO--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/o546e4gt45o4j2w4xwar.png
tags: react, tutorial, electron, typescript
series: Alarm Widget with Electron + React
---

Heya!

In this tutorial series we will build an Desktop Alarm Widget with Electron and React written in Typescript.

What will we tackle in this series:
* Typescript
* Electron
* React
* Webpack

Feature:
* Clock
* Alarm w/ notification

####**Part 1: Setting up the project**

### Start the project

Let's start! First open your terminal in the desired root folder and run the command:

```sh
npm init -y
```

This command will generate the package.json file.

```json
{
  "name": "tokei",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
}
```

### Typescript Setup

Since we want to write our application with `Typescript` we need to install it:

```sh
npm install typescript --save-dev 
```

After the installation, generate the `tsconfig.json`, for that run:

```sh
tsc --init
```

We have our project ready to write Typescript 🥳

### Electron Setup
Now we need to install Electron and setup everything related to it.

```sh
npm install electron --save-dev
```

Let's create an html file and the entrypoint for electron under `src` folder. the project structure should look like this:

```
root
- src/
--- index.html
--- main.ts
- package.json
```

Add the following content to the html file:

`./src/index.html`
```html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <div id="root"></div>
</body>

</html>
```

Now the content of the main file of our application.

`./src/main.ts`
```ts
import { app, BrowserWindow } from 'electron';

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

  win.loadFile('index.html');
}

app.on('ready', createWindow);
```

Let's add our first script command in the `package.json` to run the electron app:

```json
"scripts": {
  "build": "tsc src/main.ts",
  "start": "electron dist/main.js"
}
```

### Webpack Setup

We will use *Webpack* to optimize and build our application.
Start by installing it.

```sh
npm install webpack webpack-cli html-webpack-plugin ts-loader --save-dev
```

We will create a file to setup the webpack configs for electron, for that create `webpack.electron.js`in the root folder:

`./webpack.electron.js`
```js
const path = require('path');

module.exports = {
  // Build Mode
  mode: 'development',
  // Electron Entrypoint
  entry: './src/main.ts',
  target: 'electron-main',
  resolve: {
    alias: {
      ['@']: path.resolve(__dirname, 'src')
    },
    extensions: ['.tsx', '.ts', '.js'],
  },
  module: {
    rules: [{
      test: /\.ts$/,
      include: /src/,
      use: [{ loader: 'ts-loader' }]
    }]
  },
  output: {
    path: __dirname + '/dist',
    filename: 'main.js'
  }
}
```

Let me explain what this code does.
```js
resolve: {
  alias: {
    ['@']: path.resolve(__dirname, 'src')   
  },
  extensions: ['.tsx', '.ts', '.js'],
},
```

Now create the `webpack.config.js` in the root folder, this will consume all necessary configs for our webpack:

`./webpack.config.js`
```js
const electronConfigs = require('./webpack.electron.js');

module.exports = [
  electronConfigs
];
```

From now on don't need to navigate folder with '../../', we can use '@' as a starting point in the `src` folder.

Before:

`./src/components/button/button.ts`
```js
// Lets import from ./src/services/service1.ts
import Service1 from '../../services/service1';
```

After:

`./src/component1/component1.ts`
```js
// Lets import from ./src/services/service1.ts
import stuff from '@/services/service1';
```

Now update `npm script` in `package.json`:

```sh
"scripts": {
  "build": "webpack",
  "start": "npm run build && electron dist/main.js"
}
```

### React Setup

For our renderer we will install React and all dependencies necessary for typescript.

```sh
npm install --save-dev react react-dom @types/react @types/react-dom
```

Create the react entrypoint as `renderer.ts`:

`./renderer.ts`
```tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from '@/app/app';

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

As you can see we are importing the App Component, but we don't have it yet, let code it!

`./app/app.ts`
```tsx
import React from 'react';

const App = () => {
  return (
    <div className="app">
      <h1>I'm React running in Electron App!!</h1>
    </div>
  );
}

export default App;
```

Do you remember the  `tsconfig.json` file? 🤔 Let´s add two options to it:

```json
{
  "compilerOptions": {
    ...
    "jsx": "react",
    "baseUrl": "./",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
  }
}
```

Now setup the Webpack configuration for React, like we did for electron, we need to create a specific config file for react in the root folder:

`webpack.react.js`
```js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/renderer.tsx',
  target: 'electron-renderer',
  devtool: 'source-map',
  devServer: {
    contentBase: path.join(__dirname, 'dist/renderer.js'),
    compress: true,
    port: 9000
  },
  resolve: {
    alias: {
      ['@']: path.resolve(__dirname, 'src')
    },
    extensions: ['.tsx', '.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.ts(x?)$/,
        include: /src/,
        use: [{ loader: 'ts-loader' }]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      }
    ]
  },
  output: {
    path: __dirname + '/dist',
    filename: 'renderer.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
};
```

And update the `webpack.config.js`:

`./webpack.config.js`
```js
const electronConfigs = require('./webpack.electron.js');
const reactConfigs = require('./webpack.react.js');

module.exports = [
  electronConfigs,
  reactConfigs
];
```

### Hot Reload Setup (Optional)

To avoid running the build whenever we make any changes we will add Hot Reload, for that we need to install the following packages:
```sh
npm install nodemon webpack-dev-server electron-is-dev concurrently --save-dev
```

First we will setup the Electron Hot Reload, for that we need to create a `nodemon.json` file in the root, and add the following settings:

`nodemon.json`
```json
{
  "watch": [ 
    "src/main.ts", 
    "src/electron/*"
  ],
  "exec": "webpack --config ./webpack.electron.js && electron ./dist/main.js",
  "ext": "ts"
}
```

Now for React we need to update Webpack Configuration:

```js
...
module.exports = {
  ...
  devServer: {
    contentBase: path.join(__dirname, 'dist/renderer.js'),
    compress: true,
    port: 9000
  },
  ...
}
```

We should update our `package.json`:

```json
  ...
  "scripts": {
    "build": "webpack",
    "react:dev": "webpack serve --mode=development",
    "electron:dev": "nodemon",
    "dev": "concurrently --kill-others \"npm run react:dev\" \"npm run electron:dev\"",
    "start": "npm run build && electron dist/main.js"
  },
  ...
```

Last thing we should change our `main.js`:

```js
import { app, BrowserWindow } from 'electron';
import isDev from 'electron-is-dev'; // New Import

const createWindow = (): void => {
  let win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  });
  console.log(isDev);
  win.loadURL(
    isDev
      ? 'http://localhost:9000'
      : `file://${app.getAppPath()}/index.html`,
  );
}

app.on('ready', createWindow);
```

### SCSS Setup (Optional)

Install dependecies necessary for it:
```sh
npm install sass-loader sass style-loader css-loader --save-dev
```

Update React Webpack Configuration:

```js
...
rules: [
  ...
   {
      test: /\.s[ac]ss$/i,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader',
      ],
    }
]
```

Create you first `SCSS` file under `src/app` folder, and update you app.tsx importing it.

`./src/app/app.tsx`
```tsx
import React from 'react';
import 'app.scss'; // New import!!

const App = () => {
  return (
    <div className="app">
      <h1>I'm React running in Electron App!!</h1>
    </div>
  );
}

export default App;
```

### Conclusion of Part 1 
Finally, eveything is ready to start our application.
Let's run it!! 

```sh
npm run start
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uhlsvmxpelkeee0zg119.png)

Repository: [Tokei - Part 1 Branch](https://github.com/diink-studios/tokei/tree/part-1/the-project-setup)

[Part 2: The Tray Menu](https://dev.to/franamorim/tutorial-alarm-widget-with-electron-react-2-34dd)