Winds 2.1: Building Touch Bar Support for macOS in Electron with React
- The (limited) touch bar API that is provided by Electron
- The ipcMain module, which handles asynchronous and synchronous messages sent from a renderer process (web page)
- The ipcRenderer module, which provides a few methods that allow you to send synchronous and asynchronous messages from the renderer process (web page) to the main process (ipcMain).
In this post, we’ll do a deep dive into how we accomplished this task. Let’s do it.
The ipcMain Module
The ipcMain module is an instance of the EventEmitter class. When used in the main process, it handles asynchronous and synchronous messages sent from a renderer process (web page). Messages sent from a renderer are emitted to this module and picked up by an event handler and then passed off to a function for further processing.
Send & Receive from Electron
In /app/public/electron.js, we initialize the following code once the window is ready to show:
The event property specifies what happened, whereas the args can be a single value or an object of key-value pairs. For Winds, we chose to go with an object so we could pass along additional metadata (from the frontend), such as the current episode title and podcast name.The ipcRenderer Module
The ipcRenderer module is an instance of the EventEmitter class. It provides a few methods that allow you to send synchronous and asynchronous messages from the renderer process (web page) to the main process (Electron). Understanding how communication works was the first step in our journey to get media control support in place. To better understand how it works, let’s look at a few short code examples:
Send & Receive from React
In /app/src/components/Player.js, we use window.ipcRenderer, as ipcRenderer is not directly available, thus requiring us to pull it off of the window object:
AND So, after all is said and done, we can use the player context to differentiate between a playing episode and a paused episode. It looks something like this:React Lifecycle Events
On componentDidMount(), we use the following handler to ensure that our incoming events are picked up. Note: We wrap our code in an Electron check via the is-electron Node module to ensure that we only execute this in an Electron environment – this is important because we have web and native versions of the application.
On componentWillUnmount(), we use the following handler to ensure that all listeners are destroyed:Electron Touch Bar API
As pointed out in the previous portion of this post, we initialize ipcMain in our electron.js file. But wait, there’s more... We also have a portion of code dedicated to handling the incoming (and outbound) messages, in addition to toggling the touch bar images, and handling touch bar events:
This function should go in your main.js file, or in our case, the electron.js file.Final Product

Closing Thoughts
I hope that this mini tutorial helped shed some light on communication between the main process (Electron) and the renderer (React). As previously mentioned, the APIs aren’t entirely there yet, so you may run into some hiccups along the way – feel free to post in the comments; I’m happy to help out! If you think that I’ve missed anything, please feel free to drop a line in the comments below or find me on Twitter – @NickParsons.