Electron Adventures: Episode 4: Image Gallery with Dynamic HTML

Let's do something a little more advanced with Electron - generate some HTML dynamically, and load it into the browser window.

There's no direct functionality for this, but we can use data: URLs.

Load data URL into the web view

let { app, BrowserWindow } = require("electron")

let html = `
<!DOCTYPE html>
<html>
  <body>
    <h1>Welcome To The Internet!</h1>
  </body>
</html>
`

function createWindow() {
  let win = new BrowserWindow({})
  win.maximize()
  win.loadURL(`data:text/html;charset=utf-8,${encodeURI(html)}`)
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})

ARGV parsing

Well, we managed to pass generated HTML, but it's not doing anything interesting yet.

Let's pick a directory to display images from. The way we run Electron apps, argv starts with electron, ., so only third arg is the image directory.

let imageDir
let argv = process.argv

if (argv.length >= 3) {
  imageDir = argv[2]
} else {
  imageDir = `${__dirname}/images`
}

For testing I included a folder with some US state flags in the repo, in images.

We can check that this part worked, by displaying it in the browser:

let html = `
<!DOCTYPE html>
<html>
  <body>
    <h1>Image Gallery - ${imageDir}</h1>
  </body>
</html>

You can run this either as:

$ npx electron . /path/to/images
$ npx electron .

Find all images in directory

We can use fs and path to get all the images in a directory.

let fs = require("fs")
let path = require("path")

let findImages = (dir) => {
  let files = fs.readdirSync(dir)
  files.sort()
  return files
    .filter(x => /\.(png|jpg|jpeg|gif)/i.test(x))
    .map(x => path.join(dir, x))
}

Display images

We're almost there. Let's change the HTML to display the images. Also switching it all to dark mode, as I had enough of all white screen.

let html = `
<!DOCTYPE html>
<html>
  <head>
    <style>
      body { background-color: black; color: white; }
      img { padding: 10px; }
    </style>
  </head>
  <body>
    <h1>Image Gallery</h1>
    ${ findImages(imageDir).map(x => `<img src="file://${x}" />`).join("") }
  </body>
</html>
`

Web security

This should work but it doesn't. Let's try to figure out why.

In Electron you can go to View menu and select "Toggle Developer Tools". Or use appropriate keyboard shortcut like Cmd-Option-I. You get all the usual Chrome debugging tools!

In this case we can see that our image requests were all blocked. This makes sense. Letting data: URLs load arbitrary file: URLs doesn't sound terribly safe.

As this is just a tutorial one-off, let's disable web security rules without thinking too much about the consequences of that.

function createWindow() {
  let win = new BrowserWindow({webPreferences: { webSecurity: false }})
  win.maximize()
  win.loadURL(`data:text/html;charset=utf-8,${encodeURI(html)}`)
}

Electron app security is a big topic. If you're writing an Electron app, and not just a PWA, you likely do it because you need access to something the browser won't let you have. And if so, you're largely leaving browser's protection, and you're now responsible for security of your app.

Result

And here's what we got:

electron-adventures-04-screenshot.png

All the code for the episode is here.

See you in the next episode!