Better Blog

How to Build an Electron App Using React and Typescript

March 04, 2019

I’m starting a new project that will be a cross platform desktop application with offline capabilities. Being a developer who is proficient in JavaScript, I ultimately chose Electon as the base underlying technology to power my app. Electron allows you to use JavaScript, HTML, and CSS to develop a desktop app, while helping with some native hard parts including automatic updates, native menus, and notifications. Some huge companies use Electron to build applications including Skype, Slack, and Discord.

If your familiar with React, you probably know about create-react-app. If you don’t, create-react-app essentially allows you to create a react application with no build configuration. Say goodbye to spending hours setting up webpack configurations! Here’s a short gif of what create-react-app looks like:

Chinese Salty Egg

After doing some research I found there was an electron counterpart call Electron Forge. As someone who has never built an Electron app before, a cli that helps with development, packaging, and publishing Electron was very enticing. Unfortunately, there is a new version of Electron Forge being released (6.0), and there isn’t at the time of writing too much documentation how to get it set up, let alone making it work with Typescript and React (my go-to technologies in web development).

Getting Started

I had a few issues getting Electron Forge working by installing it globally, so I recommend that you use npx or yarn create to create your new application:

npx @electron-forge/cli@beta init my-new-app
# or
yarn create electron-app my-new-app

# then
cd my-new-app
npm start

Configuration

Once you have your application set up, you’ll have to use the webpack plugin to assist with typescript and react compilation. The primary reason we’re using webpack here is so that we can use babel and its rich ecosystem of plugins.

One you install the webpack plugin:

yarn add @electron-forge/plugin-webpack --dev

Open the package.json file, and under the forge field, make sure you have a field call plugins with the following configuration:

{
  "config": {
    "forge": {
      "plugins": [        [
          "@electron-forge/plugin-webpack",
          {
            "mainConfig": "./webpack.main.config.js",
            "renderer": {
              "config": "./webpack.renderer.config.js",
              "entryPoints": [
                {
                  "html": "./src/index.html",
                  "js": "./src/app.tsx",
                  "name": "main_window"
                }
              ]
            }
          }
        ]
      ]
    }
  }
}

Webpack

You’ll notice that there are some files in there don’t exist when electron forge scaffolded your application. Create a new file in your root directory named webpack.main.config.js and webpack.renderer.config.js (we’ll get to those shortly). Also create a file under the src directory named app.tsx, or anything else you want to call it. Make sure it ends in the tsx extension though, since we’ll be using typescript and react.

Let’s start with webpack.main.config.js. Inside the src directory, you’ll see there is a file called index.js. This file is typically called main.js in electron app tutorials, and is in charge creating windows and handling all the system events in your application. Since this file won’t be needing any React components, lets keep it simple and define the following as our webpack configuration:

module.exports = {
  entry: "./src/index.js",
}

Nothing too fancy! Not much is happening here, so lets move on to the next webpack configuration: webpack.renderer.config.js.

The electron forge webpack config does a little magic for you by doing most of the configuration. If you remember what we put in our package.json from earlier, there was 2 main options in the webpack plugin: mainConfig and renderer:

{
  "mainConfig": "./webpack.main.config.js",  "renderer": {    "config": "./webpack.renderer.config.js",
    "entryPoints": [
      {
        "html": "./src/index.html",
        "js": "./src/app.tsx",
        "name": "main_window"
      }
    ]
  }
}

Let’s focus on the renderer field here. In this field, there is a subfield called entryPoints. Under the hood the webpack plugin is taking the configuration in entryPoints and using the html as a template for HtmlWebpackPlugin which will include the javascript bundle as a script tag in the html.

Typescript and React

Now that all the plugin configuration is done, it’s time we get Typescript and React set up using babel.

Install babel, babel-loader, typescript, and the typescript/react babel presets like so:

yarn add --dev @babel-loader @babel/core @babel/preset-react @babel/preset-typescript typescript

Then add React and ReactDOM to your dependencies:

yarn add react react-dom

Once you do that, let’s configure our webpack file so that it uses the babel loader. Your webpack.renderer.config.js should look like this:

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: "babel-loader",
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
  },
}

It’s crucial that we specify the extenions in their respective places.

Finally, create a new file in the root directory called .babelrc. This is so babel can look at this file for the configuration, and we can keep our webpack config file less verbose. In the .babelrc file, add the typescript & react presets like so:

{
  "presets": ["@babel/preset-react", "@babel/preset-typescript"],
}

You can now render a react component in the app.tsx file we created earlier. For example this small application:

import * as React from "react"
import * as ReactDOM from "react-dom"

ReactDOM.render(<div>hello world</div>, document.getElementById("app"))

Now that we have all the configuration set up, there’s one last thing you need to do. The webpack plugin will create a .webpack directory with the compiled code and source map, but electron still needs to know where to put it. Inside the src/index.js file, replace the localhost url with the magic constant MAIN_WINDOW_WEBPACK_ENTRY (generated by the webpack plugin) within the loadUrl function.

mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY)

Now run yarn start and you should see your react component rendered in the desktop application!


Ju Hae Lee

Personal blog by Ju Hae Lee
I try to write code