Process Model

    Web browsers are incredibly complicated applications. Aside from their primary ability to display web content, they have many secondary responsibilities, such as managing multiple windows (or tabs) and loading third-party extensions.

    In the earlier days, browsers usually used a single process for all of this functionality. Although this pattern meant less overhead for each tab you had open, it also meant that one website crashing or hanging would affect the entire browser.

    To solve this problem, the Chrome team decided that each tab would render in its own process, limiting the harm that buggy or malicious code on a web page could cause to the app as a whole. A single browser process then controls these processes, as well as the application lifecycle as a whole. This diagram below from the Chrome Comic visualizes this model:

    Electron applications are structured very similarly. As an app developer, you control two types of processes: main and renderer. These are analogous to Chrome’s own browser and renderer processes outlined above.

    Each Electron app has a single main process, which acts as the application’s entry point. The main process runs in a Node.js environment, meaning it has the ability to modules and use all of Node.js APIs.

    The main process’ primary purpose is to create and manage application windows with the [BrowserWindow][browser-window] module.

    ```js title=’main.js’ const { BrowserWindow } = require(‘electron’)

    const win = new BrowserWindow({ width: 800, height: 1500 }) win.loadURL(‘‘)

    const contents = win.webContents console.log(contents)

    Native APIs

    To extend Electron’s features beyond being a Chromium wrapper for web contents, the main process also adds custom APIs to interact with the user’s operating system. Electron exposes various modules that control native desktop functionality, such as menus, dialogs, and tray icons.

    For a full list of Electron’s main process modules, check out our API documentation.

    Each Electron app spawns a separate renderer process for each open (and each web embed). As its name implies, a renderer is responsible for rendering web content. For all intents and purposes, code ran in renderer processes should behave according to web standards (insofar as Chromium does, at least).

    Therefore, all user interfaces and app functionality within a single browser window should be written with the same tools and paradigms that you use on the web.

    • UI styling is added through Cascading Style Sheets (CSS).
    • Executable JavaScript code can be added through <script> elements.

    Moreover, this also means that the renderer has no direct access to require or other Node.js APIs. In order to directly include NPM modules in the renderer, you must use the same bundler toolchains (for example, webpack or ) that you use on the web.

    At this point, you might be wondering how your renderer process user interfaces can interact with Node.js and Electron’s native desktop functionality if these features are only accessible from the main process. In fact, there is no direct way to import Electron’s content scripts.

    Preload scripts contain code that executes in a renderer process before its web content begins loading. These scripts run within the renderer context, but are granted more privileges by having access to Node.js APIs.

    A preload script can be attached to the main process in the BrowserWindow constructor’s webPreferences option.

    ```js title=’main.js’ const { BrowserWindow } = require(‘electron’) //… const win = new BrowserWindow({ webPreferences: { preload: ‘path/to/preload.js’ } }) //…

    ```js title=’renderer.js’ console.log(window.myAPI) // => undefined

    This feature is incredibly useful for two main purposes:

    • By exposing helpers to the renderer, you can use inter-process communication (IPC) to trigger main process tasks from the renderer (and vice-versa).