Electron Adventures: Episode 19: Sending Data To Frontend
Tags: electron, javascript
Published: 2021-08-13 00:57:11

In previous episode, we called some backend functions from the frontend through IPC channels. Now let's try it the other way.

It's similar, but not identical. Mostly because while there's one backend/main process, there could be a bunch of frontend/renderer processes, each with their window, so we can't just "send to frontend" like `ipcMain.invoke` without being a bit more specific.

So can we do `someWindow.webContents.invoke`? [Well, also no](https://github.com/electron/electron/issues/25667). For complicated technical reasons Electron decided to not include this, and if you really need it, [you need to use a third party library that emulates it with a bunch of calls](https://www.npmjs.com/package/electron-promise-ipc). I think they should just include it in Electron.

For sending one way messages we can do `someWindow.webContents.send` and that's all we'll do today.

But let's take it step by step.

### Start a new application

We start the usual way:

```shell
$ npm init -y
$ npm install --save-dev electron
```

### `index.html`

Nothing new here, just some styling, and placeholder for messages we'll receive from the backend:

```html
<!DOCTYPE html>
<html>
  <body>
    <style>
      body {
        background-color: #444;
        color: #ccc;
        font-family: monospace;
        font-size: 24px;
      }
    </style>
    <h1>Messages from the backend:</h1>
    <div id="messages"></div>
    <script src="app.js"></script>
  </body>
</html>
```

### `index.js`

We need to start a frontend as usual, except this time we also want to save the window object, so we can send messages to it.

```javascript
let { app, BrowserWindow } = require("electron")
let win

function createWindow() {
  win = new BrowserWindow({
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    }
  })
  win.loadFile("index.html")
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})
```

Now let's do the second step - let's read whatever is being typed on the terminal, and send it over!

```javascript
let readline = require("readline")

let rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.on("line", async (line) => {
  win.webContents.send("line", line)
})
```

### `app.js`

Once we receive a message, just append it to the `#messages` div.

As explained above, we can't really reply to this message, we'd need to do something a bit more complicated if we wanted responses as well.

```javascript
let { ipcRenderer } = require("electron")
let messages = document.querySelector("#messages")

ipcRenderer.on("line", (event, line) => {
  let message = document.createElement("div")
  message.textContent = line
  messages.appendChild(message)
})
```

### What to do with all this?

In a real application, you'd want to declare the interface between frontend and backend, and put it all in `preload.js`, so rest of your frontend can run without any special privileges.

### Results

And here's the result:

![Episode 19 screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7gcbn3kyuhl4n9tgf1oj.png)

As usual, [all the code for the episode is here](https://github.com/taw/electron-adventures/tree/master/episode-19-sending-data-to-frontend).