One thing I’ve always wanted to do was write some blog posts about solving coding problems. The only problem was that I was often the newbie reading the blogs for help, without much to offer of my own.

But that’s slowly changed over time. And so, since it’s something I’ve “always wanted to do,” here’s a little how-to I worked out for one of my projects. It’s not too difficult, but since I Googled for a quick solution, found nothing, and wrote my own, here it is.


I’ve been working on an Electron app in which one of the main “shiny” features is the fact that it can be re-styled or re-skinned by the user using LESS files. This isn’t that unique of an idea: one of Electron’s most famous implementations, the Atom code editor, does exactly that. My app is a different genre entirely but really benefits from this functionality.

The Concept

I’m using the ipcMain and ipcRenderer objects within Electron to have my application’s window (window.js) and “main thread” (main.js) speak to each other.

This demo assumes you already have (or know how to) set up a basic Electron application. I'm also not really going to get into how these modules do what they do, or all of the ways they could be used.1

For now I’ll simply be demonstrating a single use-case for these modules. To explain how they’ll be used, here’s their interaction in plain old English:

  1. ipcMain listens for messages coming from the application’s windows.
  2. ipcRenderer sends a message with the flag request-style and some data like the theme’s name.
  3. ipcMain hears this, and proceeds to:
    1. Load the requested .less file.
    2. Compile the .less file’s contents to CSS using the LESS module.
    3. Sends the text content (now valid CSS) back to ipcRenderer.
  4. ipcRenderer is listening for the result, and when it gets the CSS, it appends it to the window’s <style></style> element.

These steps are annotated in the code comments below. Note that some of the steps appear out-of-order because steps 1 and 3 take place in one file, and 2 and 4 in another.

This code also assumes some structure to the theme files. I plan to use a format similar to NPM’s packages with a package.json which defines which file is “the theme” and located in the app data folder. But, for the sake of demonstration, we just assume it’s a file called main.less located at src/themes/[themeName]/main.less within our app’s project folder.

The Code

// main.js

// We will be using the ipcMain module, and a few others:
const { ipcMain, app } = require("electron");  
const FS = require("fs");  
const LESS = require("less");

// 1. Listen for messages with the "request-style" flag:
ipcMain.on("request-style", ( event, name ) => {

  /*
    This assumes the themes are in the app and that the .less
    files are in folders with the theme name. How that's set
    up in practice is entirely up to you.
  */

  // 3.1. Load the requested .less file.
  let path = app.getAppPath() + "/src/themes/" + name + "/main.less";

  // 3.2. Compile the .less file's contents to CSS:
  LESS.render(
    FS.readFileSync( path, "utf8"),
    {
      paths: [ __dirname + "/../../less" ],
      relativeUrls: true
    },
    function( err, output ) {

      // 3.3. Send the CSS back to ipcRenderer:
      event.sender.send("return-style", output.css );
    }
  );
});
// window.js

// We will be using the ipcRenderer module from Electron:
const { ipcRenderer } = require("electron");

function loadTheme( name ) {

  // 2. Send a message to the main thread
  ipcRenderer.send("request-style", name );

  // 4. Listen for async-reply message from main process:
  ipcRenderer.on("return-style", (event, css) => {

    // Get the correct element from the HTML (a <style> tag):
    let element = document.getElementById("user-style");

      // Replace whatever is in it with our new CSS:
      element.innerHTML = css;
    });
  }
}

That’s it!


Thanks for reading! If you have any questions or find any mistakes you can find me on Twitter or email me at ian [dot] paschal [at] gmail [dot] com.


  1. This blog post does a good job showing the possibilities. These modules are useful for lots of things and in my opinion should be one of the first lessons for starting a project with Electron.

Code snippets are covered by the MIT License.