Inter-Process Communication (IPC) in ElectronJS

ElectronJS is used for building Cross-Platform Native Desktop applications using web technologies such as HTML, CSS, and JavaScript which are capable of running on Windows, macOS, and Linux Operating Systems. It combines the Chromium engine and NodeJS into a Single Runtime. Several notable open-source projects such as Visual Studio Code, Slack, Atom, Postman and Brave Browser are developed using Electron.

Electron can be categorized into two main processes:-

  • Main Process
  • Renderer Process

IPC in Electron: A single main process can have multiple renderer processes. Every Renderer Process can be thought of as a new tab in the Browser. In this tutorial, we will discuss how Electron communicates between these processes using Inter-Process Communication (IPC). Electron provides us with two IPC Modules to help communicate between the processes,

  • ipcMain:This Module is used to communicate from the Main Process to the Renderer Processes. It is defined and used in the Main Process. It handles all synchronous and asynchronous messages being sent from the renderer process.
  • ipcRenderer: This Module is used to communicate from the Renderer Processes to the Main Process. It is defined and used in the Renderer Processes. It provides the capability to send messages to and receive messages from the Main Process, synchronously and asynchronously.

It is strongly recommended to not perform heavy computations in the Renderer Processes to prevent the application from slowing down in performance and from being more resource-intensive. Instead, we should use IPC to distribute these tasks to the Main Process and let the Main Process handle any heavy computations in the application. There are three major differences between Synchronous Data Transfer and Asynchronous Data Transfer in IPC,

  1. The ipc.send() method is used for Asynchronous Data Transfer, whereas for Synchronous Data Transfer ipc.sendSync() method is used instead.
  2. We need to specifically implement a callback function to handle the Response coming in from the Main Process in Asynchronous Data Transfer. In case of Synchronous Data Transfer we do not need to implement the callback function since ipc.sendSync() method will return the data.
  3. In the Main Process (main.js file), win.webContents.send() method is used for Asynchronous Data Transfer. This can be replaced with event.returnValue() method for Synchronous Data Transfer.

Note: This Tutorial also assumes you are familiar with the prerequisites as covered in the above-mentioned link.



Project Structure: Let’s start with the building blocks of the Tutorial,

  • Step 1: Check whether node and npm are installed. If not then visit below artiles:

  • Step 2: Navigate to an Empty Directory to setup the project, and run the following command,
    npm init

    Follow the steps given to generate the package.json file

  • Step 3: Make sure that the Electron is installed if not then install it now.
  • Step 4: Create a main.js file according to the project structure. This file is the Main Process and acts as an entry point into the application.
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    const { app, BrowserWindow } = require('electron')
    let win;
      
    function createWindow() {
      // Create the browser window.
      win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          nodeIntegration: true
        }
      })
      
      // and load the index.html of the app.
      win.loadFile('src/index.html')
      
      // Open the DevTools.
      // win.webContents.openDevTools()
      
      //Quit app when main BrowserWindow Instance is closed 
      win.on('closed', function () {
        app.quit();
      });
    }
      
    // This method will be called when the Electron has finished
    // initialization and is ready to create browser windows.
    // Some APIs can only be used after this event occurs.
    app.whenReady().then(createWindow)
      
    // Quit when all windows are closed.
    app.on('window-all-closed', () => {
      // On macOS 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 macOS 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 (BrowserWindow.getAllWindows().length === 0) {
        createWindow()
      }
    })

    chevron_right

    
    

  • Step 5: Create the index.html file within the src directory. The index.html is rendered in its individual Process by the main.js file on Application Startup. Every Renderer Process can have its own associated CSS and JavaScript file as well.
    In index.html

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    <!DOCTYPE html>
    <html>
      
    <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>
        
        <meta http-equiv="Content-Security-Policy" 
              content="script-src 'self' 'unsafe-inline';" />
    </head>
      
    <body>
        <h1>Hello Geeks!</h1>
        <div>
            We are using node
            <script>
                document.write(process.versions.node)
            </script>, Chrome
            <script>
                document.write(process.versions.chrome)
            </script>, and Electron
            <script>
                document.write(process.versions.electron)
            </script>.
        </div>
        <script src="index.js"></script>
    </body>
      
    </html>

    chevron_right

    
    

  • Step 6:To launch the Electron Application, run the Command, “start” being the script which we have defined in the package.json file.
    npm start
  • Output:

Asynchronous Data Transfer: Since we have set up the Basic Electron Application, let us define a new BrowserWindow Instance which will render a new webpage. This new webpage is going to be the new-window.html file. We will then implement Asynchronous IPC to communicate data between new-window.html file and index.html file.

  • new-window.html:
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>New Window</title>
        <meta http-equiv="Content-Security-Policy" 
              content="script-src 'self' 'unsafe-inline';" />
    </head>
    <body>
        <div>New Window Opened !</div>
          
        <br>
        <input type="text">
        <button id="submit">Pass Value to Main Window</button>
      
        <script src="new-window.js"></script>
    </body>
    </html>

    chevron_right

    
    

  • index.html: As of now, this webpage is not being called from anywhere within the application. To change this insert the following code into index.html file just above the script tag.
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    <h3>Aynschronous Message Sending</h3>
    <div>
        Value Received From Renderer Process - 
      <span id="value"></span>
    </div>
    <br>
    <button id="new">
      Click Me to Open New Window
    </button>

    chevron_right

    
    

  • index.js:
    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    const electron = require('electron')
    const path = require('path')
    // BrowserWindow Instance is a part of the Main Process, 
    // To fetch its instance from the Main Process,
    // Use electron.remote
    const BrowserWindow = electron.remote.BrowserWindow
      
    var update = document.getElementById('value');
    var button = document.getElementById('new');
      
    button.addEventListener('click', function (event) {
        // Linking to new-window.html
        const newPath = path.join('file://', __dirname, 'new-window.html');
        let win = new BrowserWindow({
            // To display the Default Frame of the Window 
            // consisting of default Menu
            frame: true
              
            // Makes the Renderer Window Sticky, 
            // Will always stay on top despite focus change 
            alwaysOnTop: true,
            width: 600, 
            height: 400, 
            webPreferences: {
                nodeIntegration: true
            }
        });
      
       // Destroy the BrowserWindow Instance on close 
        win.on('close', function () {
            win = null;
        });
      
        // win.webContents.openDevTools();
        win.loadURL(newPath);
        win.show();
    });

    chevron_right

    
    

  • Output: With this our GUI is ready. Upon launching the application.
  • To pass the data from the “input” tag in new-window.html to index.html using Asynchronous IPC perform the following steps:

    • Step 1: In new-window.js file,
      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      const electron = require('electron')
      const remote = electron.remote;
      // Import the ipcRenderer Module from Electron 
      const ipc = electron.ipcRenderer;
        
      var input = document.querySelector('input');
      var submit = document.getElementById('submit');
        
      // Adding Click EventListener to the Button
      submit.addEventListener('click', function () {
          console.log(input.value);
        
          // Calling the ipcRenderer.send() 
          // To send the value from the input tag with 
          // a Unique Key to the main process
          // Asynchronously
          ipc.send('update-value', input.value);
          remote.getCurrentWindow().close();
      });

      chevron_right

      
      

    • Step 2: In the main.js file, import the ipcMain module from electron.
      const ipcMain = require('electron').ipcMain

      Add the following at the end of the file,

      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      // Responsible for Communication of data from Main
      // process to the Renderer Process
      // Received the value send from the new-window.js file 
      // Identifies the data passed based on the Key 
      // Which was set in the ipc.send() method in new-window.js file   
      ipcMain.on('update-value', function (event, arg) {
        console.log(arg);
          
        // Passing the data from Main Process to index.html
        // BrowserWindow Instance, value will be received in 
        // the index.js based on the Key set here. Using the 
        // 'win.webContents.send' method for Asynchronous Data Transfer
        win.webContents.send('updateValue', arg);
      });

      chevron_right

      
      

    • Step 3: In index.js file, import the ipcRenderer module from electron
      const ipc = electron.ipcRenderer;

      Add the following at the end of the file,

      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      // Using the ipcRenderer.on() method 
      // Implementing the Callback Function for Asynchronous IPC, 
      // To receive the data based on the key set in the main.js file 
      ipc.on('updateValue', function(event, arg) {
          console.log(arg);
            
          // Updating the value of the HTML Tag with the Data Received
          // In Case the Data Received is not a Number and is 
          // some arbitary Value,display will show as NaN (Not a Number)
          update.innerHTML = Number(arg);
      });

      chevron_right

      
      

    • Output: We have successfully Implemented Asynchronous IPC.

    Synchronous Data Transfer: We will now implement Synchronous IPC between the Main Process and Renderer Process and visualize the differences between them as explained above.

    • Step 1: In the index.html file add the following code just after the button tag and before the script tag.
      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      <br>
      <h3>Synchronous Message Sending</h3>
      <div>Value Received From Main Process - 
        <span id="received"></span>
      </div>
      <br>
      <button id="send">
        Click Me for Synchronous Message
      </button>

      chevron_right

      
      

    • Step 2: The ‘Click Me for Synchronous Message’ button does not have functionality associated with it. We will add EventListener to the button by adding the following code in the index.js file.
      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      var received = document.getElementById('received')
      var button2 = document.getElementById('send');
        
      // Adding Click EventListener to button2
      // For Synchronous Message Transfer we are using the 'ipc.sendSync' method
      // We do not need to Implemented any Callbacks to handle the Response 
      // The 'ipc.sendSync' method will return the data from the Main Process 
      button2.addEventListener('click', function(event) {
        
          // Setting the Key and the Message to be sent to the Main Process
          const data = ipc.sendSync('synchronous', 'Message to Main Window');
        
          // Setting the Data received to the <span> tag
          received.innerHTML = data;
      });

      chevron_right

      
      

    • Step 3: In the main.js file, add the following code at the end of the file,
      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      ipcMain.on('synchronous', (event, arg) => {
        
        // Using this method instead of 'win.webContents.send' 
        // for Synchronous Message Transfer 
        // The Value of arg = 'Message to Main Window'
        // In case we do not use 'event.returnValue', We will
        // get the following Error 
        // 'Uncaught Error: Unable to deserialize cloned data due to 
        // invalid or unsupported version.'
        event.returnValue = 'Synchronous Message Sent';
      });

      chevron_right

      
      

    • Output:



    My Personal Notes arrow_drop_up

    Check out this Author's contributed articles.

    If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

    Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


    Article Tags :

    Be the First to upvote.


    Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.