Building Electron Applications with Multiple Pages

If you don’t want to dive into webpack/framework build frame, but need an easy way to use multiple pages in electron with simple navigation, this might work for you.

There are a few approaches to handling multiple pages in an electron app. Since chromium comes bundled with electron, essentially a web browser, and electron runs a node application, one approach is just to serve multiple pages as external html files. The same way you might on a web server. You can load these external pages, locally or remotely, into the current BrowserWindow via links or api calls. But results aren’t as seamless as an application. There’s a notorious screen flash that happens sometimes when the external page loads. Though mitigated somewhat with tweaks, electron is really designed for single page/cached content applications.

Multiple pages can be implemented through views, available in various frameworks with architectures for routing and templating, like Vue, React and Angular. These assemble pages by building custom objects loaded into memory. But if we don’t want to use a framework with their attendant complexities, and don’t want janky window flashing from hard links, there’s a third alternative.

Deep in the source for lies a useful approach. I decided to simplify, deconstruct and generalize it here. I’ll also integrate Bootstrap 4 to leverage its tab components to do the actual navigation. Because, lazy.

To build from scratch:

1. git clone
2. cd electron-quick-start
3. npm install bootstrap
4. npm install jquery
5. npm install popper.js
6. npm install

Now, in the head of index.html add:

<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">

In the script section of the same index.html add:

window.$ = window.jquery = require("jquery");
window.popper = require("popper.js");

Now the electron app is fully wired for bootstrap. Next we’ll create external templates for each of our pages. Here’s an example of a template for the main page created in an external file called main.html:

<div class="container template" id="main-template" data-tab="main">
<!-- all the stuff for the main page goes here... -->

Create one of these files for each page in the application. Ensure that each has its own unique id and data-tab value. Next let’s go back and tweak the index.html to reference the templates. In the head section of the html:

<link rel="import" href="main.html">
<link rel="import" href="settings.html">
<link rel="import" href="help.html">
<link rel="import" href="contact.html">

for as many template pages as we created.

Next add the following to the script block in the same index.html file:

const links = document.querySelectorAll('link[rel="import"]');
links.forEach((link) => {
    let template = link.import.querySelector('template');
    let clone = document.importNode(template.content, true);
    let target = clone.querySelector(".template");

What this script fragment does is look for every import link in the head section, which it then loads and caches in memory as a document fragment and appends to an html element in the dom with an id that matches the id referenced by the data-tab in the template. Templates are a very cool, seldom utilized, feature baked into HTML5.

So all that’s left is replacing the body of index.html with the following to provide navigation and some slots and scaffolding for the pages.

<div class="container my-4"><div class="row"><div class="col-2">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">

<a class="nav-link active" id="home-tab" data-toggle="pill" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
<a class="nav-link" id="about-tab" data-toggle="pill" href="#about" role="tab" aria-controls="about" aria-selected="false">About</a>
<a class="nav-link" id="contact-tab" data-toggle="pill" href="#contact" role="tab" aria-controls="contact" aria-selected="false">Contact</a>
<a class="nav-link" id="settings-tab" data-toggle="pill" href="#settings" role="tab" aria-controls="settings" aria-selected="false">Settings</a>


<div class="col-10"><div class="tab-content" id="v-pills-tabContent">

<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab"></div>
<div class="tab-pane fade" id="about" role="tabpanel" aria-labelledby="about-tab"></div>
<div class="tab-pane fade" id="contact" role="tabpanel" aria-labelledby="contact-tab"></div>
<div class="tab-pane fade" id="settings" role="tabpanel" aria-labelledby="settings-tab"></div>


Now run:

npm start

and voila. A multipage electron application using HTML5 templates and bootstrap for easy styling and navigation.

Complete source code available on

(For a quickstart on electron in general, along with a solid discussion of options for packaging, updating and software certificates, I found this concise resource very well put together:

the strangstein

or “skein rock” in German.

found in an excavation. I’m not at liberty to say where, but it was NOT the Vordarian Beltway. Looks to be slightly used. The glyphs are still running.

25 Bitcoins or best offer.

Cannot ship due to localized temporal distortions created by the artifact. Must arrange for pick-up. Earth sector: 37°14’31.1″N 115°49’08.9″W

Click on image for real time video feed of device in operation.

alien artifact

Lissajous Curves

Lissajous figures were developed by a French mathematician in 1857 to visually explore the nature of sound. He taped mirrors on two vibrating tuning forks, bounced light-beams off them and projected the intersecting patterns of harmonic vibrations on the wall.

This can be done with code as well by tweaking sine waves, the building blocks of sound and vibration, to emulate a pair of virtual tuning forks using the following formula:

x=\sin(at+\delta),\quad y=B\sin(bt)

This widget explores Lissajous shapes with code. You can tweak the formula with the sliders on the upper right and save any image you create. The widget cheats a little in the name of artistic license and visualization by connecting points of the curves and extends the formula a bit with additional parameters in lieu of fiddling with actual tuning forks.