PairUp: An Electron + React App


PairUp is an electron and react desktop application built with an embedded database, ChakraUI components, some Neumorphic cards, and plenty of Katerina Limpitsouni's famous svg illustrations.


A great example of pushing the limits of the javascript language, Electron uses Nodejs and Chromium to let you write cross platform desktop applications in Javascript! This project is a great example of the power brought to javascript by such a large community.

React in Electron

I decided to use React in Electron for two reasons.

  1. I was very interested to learn how it would work to connect the two practically.
  2. When I setup a few pages to test in electron using vanilla js and html I found a flash to blank screen when loading one html page to the next, similar to the delay from one page to the next found on the internet.
Just give me an excuse, any will do

Now I expect that this had to do with either my development set up, or the way I was linking around, and could be probably be smoothed out. However that was all the excuse that reason #1 needed to take charge.

The result was beautiful and smooth, just like on the web react creates smooth transitions and responsive applications. Here are some resources that may be helpful for react+electron projects:

Main and Renderer Processes

Electron has two processes, the Main Process and the Renderer Process. Similar to the client and server side processes working together on a webapp. The main process communicates with the computer and its native functions; the file system, the webcam, the native menus, keyboard shortcuts, the window dimensions, and more. The renderer process holds the user interface with all its buttons, fonts, images, and any associated business logic.


There are a few steps in order to get electron and react to communicate.

Electron's has some methods for communication within the ipcRenderer and ipcMain modules.

This works very similar to post requests with fetch between the client and serverside of a web application. Here is an example:

// In the main process we are creating a channel, similar to an event listener
ipcMain.on('get-participant-list', async (event, arg) => {
// When a message is sent on the get-participant-list channel
let participants = await db.participants.find({}); //find all participants
event.reply('return-participants-list', participants) //Send back the updated participant list on the return-participants-list channel.

Likewise in the renderer process (App.js inside create-react-app in this case)

// Here we create the listener function with event and args as two parameters.
let participantListener = (event, args) => {
// When this listener gets called we will set the Participants state to the data included in the args parameter.
// And here we open the channel to receive a the message from the main process.
window.electron.ipcRenderer.on('return-participants-list', participantListener)

This way we can keep our state up to date with the data in the database and keep the UI current and dynamic.

I wanna go fast

One nice difference between communicating with channels and fetching data from a database api is that, in the case of Electrons channels, the wait time is unnoticeable. The communication all takes place on the same computer, even within the same program, this means we can be a bit more liberal with database calls.

While some applications of a larger scale may require optimizing channels when passing large amounts of data, for our use case this was unnecessary. This leaves us free to make large calls more frequently, keep the code clean, with no tradeoffs.

Speaking of databases, ours is baked right into the program!