Contribute to help us improve!

Are there edge cases or problems that we didn't consider? Is there a technical pitfall that we should add? Did we miss a comma in a sentence?

If you have any input for us, we would love to hear from you and appreciate every contribution. Our goal is to learn from projects for projects such that nobody has to reinvent the wheel.

Let's collect our experiences together to make room to explore the novel!

To contribute click on Contribute to this page on the toolbar.

Add Electron to an Angular application using Angular CLI

This cookbook recipe explains how to integrate Electron in an Angular 10+ application. Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS. As an example, very well known applications as Visual Studio Code, Atom, Slack or Skype (and many more) are using Electron too.

At the moment of this writing Angular 11.2.0, Electron 11.2.3 and Electron-builder 22.9.1 were the versions available.

Here are the steps to achieve this goal. Follow them in order.

Add Electron and other relevant dependencies

There are two different approaches to add the dependencies in the package.json file:

  • Writing the dependencies directly in that file.

  • Installing using npm install or yarn add.

Please remember if the project has a package-lock.json or yarn.lock file use npm or yarn respectively.

In order to add the dependencies directly in the package.json file, include the following lines in the devDependencies section:

"devDependencies": {
...
    "electron": "^11.2.3",
    "electron-builder": "^22.9.1",
...
},

As indicated above, instead of this npm install can be used:

$ npm install -D electron electron-builder

Or with yarn:

$ yarn add -D electron electron-builder

Create the necessary typescript configurations

In order to initiate electron in an angular app we need to modify the tsconfig.json file and create a tsconfig.serve.json and a tsconfig.base.json in the root folder.

tsconfig.json

This file needs to be modified to create references to ./src/tsconfig.app.json and ./src/tsconfig.spec.json to support different configurations.

{
  "files": [],
  "references": [
    {
      "path": "./src/tsconfig.app.json"
    },
    {
      "path": "./src/tsconfig.spec.json"
    }
  ]
}

tsconfig.app.json

{
  "extends": "../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../app",
    "module": "es2015",
    "baseUrl": "",
    "types": []
  },
  "include": [
    "**/*.ts",
  ],
  "exclude": [
    "**/*.spec.ts"
  ],
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true,
    "preserveWhitespaces": true
  }
}

tsconfig.spec.json

{
  "extends": "../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../spec",
    "module": "commonjs",
    "types": [
      "jasmine",
      "node"
    ]
  },
  "files": [
    "test.ts",
  ],
  "include": [
    "**/*.spec.ts",
    "**/*.d.ts"
  ],
  "exclude": [
    "dist",
    "release",
    "node_modules"
  ]
}

tsconfig.base.json

This is shared between tsconfig.app.json and tsconfig.spec.json and it will be extended on each config file.

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "es2016",
      "es2015",
      "dom"
    ]
  },
  "files": [
    "electron-main.ts"
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.d.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

tsconfig.serve.json

In the root, tsconfig.serve.json needs to be created. This typescript config file is going to be used when we serve electron:

{
  "compilerOptions": {
    "outDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom"
    ]
  },
  "include": [
    "electron-main.ts"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

Add Electron build configuration

In order to configure electron builds properly we need to create a new json on our application, let’s call it electron-builder.json. For more information and fine tuning please refer to the Electron Builder official documentation.

The contents of the file will be something similar to the following:

{
  "productName": "devon4ngElectron",
  "directories":{
    "output": "./builder-release"
  },
  "win": {
    "icon": "dist/assets/icons",
    "target": [
      "portable"
    ]
  },
  "mac": {
    "icon": "dist/assets/icons",
    "target": [
      "dmg"
    ]
  },
  "linux": {
    "icon": "dist/assets/icons",
    "target": [
      "AppImage"
    ]
  }
}

There are two important things in this files:

  1. "output": this is where electron builder is going to build our application

  2. "icon": in every OS possible there is an icon parameter, the route to the icon folder that will be created after building with angular needs to be used here. This will make it so the electron builder can find the icons and build.

Modify angular.json

angular.json has to to be modified so the project is build inside /dist without an intermediate folder.

{
  "architect": {
    "build": {
      "outputPath": "dist"
    }
  }
}

Create the electron window in electron-main.ts

In order to use electron, a file needs to be created at the root of the application (main.ts). This file will create a window with different settings checking if we are using --serve as an argument:

import { app, BrowserWindow } from 'electron';
import * as path from 'path';
import * as url from 'url';

let win: any;
const args: any = process.argv.slice(1);
const serve: any = args.some((val) => val === '--serve');

const createWindow:any = ()=>{
  // Create the browser window.
  win = new BrowserWindow({
    fullscreen: true,
    webPreferences: {
      nodeIntegration: true,
    }
  });

  if (serve) {
    require('electron-reload')(__dirname, {
      electron: require(`${__dirname}/node_modules/electron`)
    });
    win.loadURL('http://localhost:4200');
  } else {
    win.loadURL(
      url.format({
        pathname: path.join(__dirname, 'dist/index.html'),
        protocol: 'file:',
        slashes: true
      })
    );
  }

  if (serve) {
    win.webContents.openDevTools();
  }

  // Emitted when the window is closed.
  win.on('closed', () => {
    // Dereference the window object, usually you would store window
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    // tslint:disable-next-line:no-null-keyword
    win = null;
  });
}

try {
  // 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);

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

   app.on('activate', () => {
    // On OS X 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 (win === null) {
      createWindow();
    }
  });
} catch (e) {
  // Catch Error
  // throw e;
}

Add the electron window and improve the package.json scripts

Inside package.json the electron window that will be transformed to electron-main.js when building needs to be added.

{
  ....
  "main": "electron-main.js",
  "scripts": {...}
  ....
}

The scripts section in the package.json can be improved to avoid running too verbose commands. As a very complete example we can take a look to the My Thai Star’s scripts section and copy the lines useful in your project. In any case, at least we recommend to add the following lines:

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "electron:tsc": "tsc -p tsconfig.serve.json",
    "electron:run": "npm run electron:tsc && ng build --base-href ./ && npx electron .",
    "electron:serve": "npm run electron:tsc && npx electron . --serve",
    "electron:pack": "npm run electron:tsc && electron-builder --dir --config electron-builder.json",
    "electron:build": "npm run electron:tsc && electron-builder --config electron-builder.json build"
  },

The electron: scripts do the following:

  • electron:tsc: Compiles electron TS files.

  • electron:run: Serves Angular app and runs electron.

  • electron:serve: Serves electron with an already running angular app (i.e. a ng serve command running on another terminal).

  • electron:pack: Packs electron app.

  • electron:build: Builds electron app.

Add Electron to an Angular application using Nx CLI

Creating an Electron app is very easy and straight-forward if you are using Nx CLI. As a pre-requisite, you should already have an application in your Nx workspace which you want to run as a front-end in your Electron app. (You can follow this guide if you want to get started with Nx).

Follow the steps below to develop an Electron app in your Nx workspace:

Install nx-electron

Install nx-electron using the command:

  npm install -D nx-electron

This will add the packages electron and nx-electron as dev dependencies to your Nx workspace. This will help us generate our Electron app in the next step.

Generate your Electron app

Once you have installed nx-electron, you can generate your electron app using the command:

  nx g nx-electron:app <electron-app-name> --frontendProject=<frontend-app-name>

And that is it! You have generated your Electron app already. All the configuration files (tsconfig.*) are generated for you under <electron-app-name> in your Nx workspace.

Serving your app

You can use this command to serve your Electron app:

  nx run-many --target=serve --projects=<frontend-app-name>,<electron-app-name> --parallel

If you see a blank application, it is because the Electron app was served before the front-end was served. To avoid this, you can serve the front-end and back-end separately, (that is, serve the back-end only after the front-end is served).

Building your app

The command for building your Electron app in Nx is similar to the serve command above, you only change the target from serve to build:

  nx run-many --target=build --projects=<frontend-app-name>,<electron-app-name> --parallel

Packaging your app

Make sure you have build your app before you try to package it using the following command:

  nx run <electron-app-name>:package [--options]

The options that can be passed can be found here.

You can find a working example of an Electron app in devon4ts-samples.