DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on • Updated on

Electron Adventures: Episode 19: Sending Data To Frontend

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. 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. 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:

$ npm init -y
$ npm install --save-dev electron
Enter fullscreen mode Exit fullscreen mode

index.html

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

<!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>
Enter fullscreen mode Exit fullscreen mode

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.

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()
})
Enter fullscreen mode Exit fullscreen mode

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

let readline = require("readline")

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

rl.on("line", async (line) => {
  win.webContents.send("line", line)
})
Enter fullscreen mode Exit fullscreen mode

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.

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)
})
Enter fullscreen mode Exit fullscreen mode

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

As usual, all the code for the episode is here.

Discussion (0)