How to Build Your First Desktop App with JavaScript Using Electron

Have you ever wanted to build a desktop application using your web development skills? With Electron, you can create cross-platform desktop apps with HTML, CSS, and JavaScript.

Electron is an open source framework developed by GitHub. It combines the Chromium rendering engine and the Node.js runtime, allowing you to build desktop GUI applications using familiar web technologies.

Whether you‘re a seasoned web developer looking to create desktop apps or just getting started with JavaScript, Electron is a great choice. In this tutorial, I‘ll walk you through the process of building your first desktop app with Electron from scratch.

What is Electron?

Electron is a framework for building native desktop applications with web technologies. It was initially created for the Atom text editor in 2013 and open sourced in 2014.

Some key features of Electron include:

  • Cross-platform compatibility (Windows, macOS, Linux)
  • Built on Chromium and Node.js
  • Uses HTML, CSS, and JavaScript for the GUI
  • Can call Node.js modules directly from the DOM
  • Supports native menus, dialogs, and notifications
  • Automatic app updates
  • Debugging and profiling with Chrome Developer Tools
  • Crash reporting and logging

Popular apps built with Electron include Visual Studio Code, Slack, Discord, Skype, and many more. Electron makes it easy to leverage existing web development skills to build powerful desktop apps that can run on multiple operating systems.

Electron Application Architecture

Before we dive into building an app, it‘s important to understand the basic architecture of an Electron application. Electron apps consist of two types of processes:

  1. The main process
  2. One or more renderer processes

The main process runs the application‘s entry point, a script specified in the package.json file. This process creates renderer processes and web pages by creating BrowserWindow instances. It has access to the full Node.js API and is responsible for lifecycle events like launching the app, quitting, sleeping, etc.

Renderer processes manage individual web pages in the app. Each web page runs in its own process to prevent a crash in one page from taking down the whole application. Renderer processes communicate with the main process via interprocess communication (IPC).

This two-process architecture enables some key benefits:

  • Web pages cannot directly affect native GUI elements belonging to other processes
  • Apps are more performant since resource-intensive tasks can be offloaded to background processes
  • Renderer processes can crash without crashing the whole app

Setting Up an Electron App

With the basic concepts covered, let‘s set up an Electron app from scratch. You‘ll need Node.js installed to get started. I recommend using the latest LTS version.

First, create a new directory for your project:

mkdir my-electron-app
cd my-electron-app

Next, initialize a new Node.js project and install the electron package:

npm init -y
npm install --save-dev electron

Open the package.json file and add a "start" script:

{
  "name": "my-electron-app",
  "version": "1.0.0",
  "description": "My first Electron app",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "^13.0.0"
  }
}

This tells Electron to look for a main.js file to start the app.

Now create the main.js file:

const { app, BrowserWindow } = require(‘electron‘)

function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
  })

  win.loadFile(‘index.html‘)
}

app.whenReady().then(() => {
  createWindow()
})

app.on(‘window-all-closed‘, function () {
  if (process.platform !== ‘darwin‘) app.quit()
})

This code does a few key things:

  1. Imports the app and BrowserWindow modules from Electron
  2. Defines a createWindow function that creates a new 800×600 BrowserWindow and loads index.html
  3. Calls createWindow when the app is ready
  4. Listens for the window-all-closed event and quits the app on non-macOS platforms

Finally, create an index.html file:

<!DOCTYPE html>
<html>
<head>
    <title>Hello World!</title>
</head>
<body>

    <p>Welcome to your Electron app.</p>
</body>
</html>

You can now launch the app with:

npm start

You should see a basic window displaying your "Hello World!" HTML page. Congratulations, you just built your first Electron app!

From here, you can start fleshing out your HTML/CSS to create the app‘s GUI just like you would for a web page. The renderer process has full access to the DOM and browser APIs.

Communicating Between Processes

Many apps need to exchange data between the main and renderer processes. Electron provides a few different ways to enable this interprocess communication:

  1. ipcRenderer and ipcMain modules
  2. remote module
  3. Custom DOM events

The ipcRenderer and ipcMain modules allow you to send messages between processes synchronously and asynchronously.

In the renderer process:

const { ipcRenderer } = require(‘electron‘)

ipcRenderer.send(‘synchronous-message‘, ‘ping‘)

In the main process:

const { ipcMain } = require(‘electron‘)

ipcMain.on(‘synchronous-message‘, (event, arg) => {
  console.log(arg) // prints "ping"
  event.returnValue = ‘pong‘
})

The remote module allows the renderer process to access main process modules via a remote object:

const { BrowserWindow } = require(‘electron‘).remote

let win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL(‘https://github.com‘)

Custom DOM events offer another way for the renderer process to send information to the main process:

document.getElementById(‘send-data‘).addEventListener(‘click‘, () => {
  window.dispatchEvent(new CustomEvent(‘user-data‘, {
    detail: { name: ‘John‘, age: 30 }
  }));
})

In the main process:

const { ipcMain } = require(‘electron‘)

ipcMain.on(‘user-data‘, (event, data) => {
  console.log(data) // { name: ‘John‘, age: 30 }
})

Use the approach that makes the most sense for your application‘s needs.

Packaging and Distribution

Once your app is ready for primetime, you‘ll want to package it for distribution. Electron apps can be packaged for Windows, macOS, and Linux from the same codebase using tools like electron-packager or electron-builder.

With electron-packager, you specify options like the platform, architecture, icon, and output directory:

npx electron-packager . app-name --platform=win32 --arch=x64 --icon=assets/icon.ico --out=dist

This will output an executable for Windows x64 in the dist directory.

Updating an Electron app is also much easier than traditional desktop apps thanks to built-in support for Squirrel, an auto-update framework. Electron apps can automatically download and install updates in the background with minimal user interruption.

Best Practices

Here are some best practices to keep in mind when building Electron apps:

  • Keep the main process lean by offloading CPU-intensive tasks to renderer processes or worker threads. Avoid blocking the main process to keep your app responsive.
  • Limit the use of remote as it has performance and security implications. Only use it when absolutely necessary.
  • Be mindful of app size as Electron bundles Chromium and Node.js. Watch out for large dependencies that can quickly balloon your app. Prune unused Node.js APIs and consider compression.
  • Follow security best practices like validating user input, sanitizing output, and keeping dependencies updated to patch known vulnerabilities.
  • Use the Chrome Developer Tools to profile, debug, and optimize your app‘s performance.

Useful Resources

Here are some great resources for learning more about Electron:

Electron also has a vibrant community on Twitter, Slack, Discord, forums, and more. Don‘t hesitate to reach out if you need help!

Conclusion

Electron makes it incredibly easy for web developers to use their existing skills to build powerful, cross-platform desktop apps. Its architecture, extensive APIs, and active community make it a compelling choice.

In this tutorial, we covered the basics of Electron application architecture, set up a simple app from scratch, implemented interprocess communication, and discussed packaging, distribution, and best practices.

I encourage you to extend the basic "Hello World!" app we built and explore Electron‘s many features like native menus, dialogs, tray icons, and more. With some creativity and effort, you can build some truly awesome desktop apps without having to learn a new language or framework.

Happy coding!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *