Progressive Web App and Electron architecture
At Euphoric Adventures, we’re building Euphoria, a Progressive Web App, and packaging it with Electron to create an experience that rocks from iPhone to Google Chrome to OS X. Today I want to talk about how we made it happen and provide some architecture guidance which will make your own cross platform journey easier.
Step 1: Make a Progressive Web App
Progressive Web Apps (PWA) are apps that blur the boundary between the web and native environments. Although there’s no formal definition of what makes a web app progressive, if you meet these criteria we’d happily give you the PWA badge:
Make your web app work well and look nice on any screen size by making it responsive. This is best achieved by styling “mobile first” and then adding more style definitions for bigger screens with media queries. For example, hamburger navigation can be fully expanded on load when there’s more screen real estate; images will not be full width and so on.
As well as styling “mobile first”, “offline first” is a must for any PWA. This means that the majority of your web app should be usable without an Internet connection, just like good native apps.
You can do this with self-concocted Service Worker code which caches web requests, or if you follow our suggested architecture, everything that Webpack outputs can be safely cached and you can use offline-plugin which backs up Service Workers with the older AppCache method to support iOS. Nice!
Fast and native-feeling
This is pretty vague, but your web app needs to be fast and feel like the native apps you use every day.
Being “offline first” will be a huge first step, avoiding costly new page loads on open. Using something like ui-router will transform the costly page load navigations of 2010 into 2017 ‘state transitions’, using HTML5’s History API to let the user move backwards and forwards as if real pages were visited.
Using a component library like Angular Material will give your UI a native feel and also save significant design and development time.
Step 2: Bundle with Webpack
Step 3: Understand life with Electron
If you’re using Webpack, you get a single directory output of your web app; probably a
Before we go ahead and package this directory up with Electron, let me be clear about two ‘drawbacks’:
Most developers have never used system level accessibility features. The advantage of any native app is that accessibility features and tools can easily understand the app’s structure: it’s easy to know a button’s a button (and thus an action) because the button is a real native button. By default, Electron apps don’t have it this easy. Researching how to make accessible web apps is really important! Here are my very limited top tips:
- Use semantic elements like
- Use ARIA attributes to provide semantics where they’re not obvious
- Correctly log navigation through routing so the back button works, instead of using jQuery or
ng-if(and so on) to provide a user journey
- Try a screen reader! Guidance will only go so far. Try and complete your app’s critical path with a screen reader to understand your users’ needs.
Size and Memory
Electron includes a modified copy of the Chromium runtime. This makes even small apps have a significant file size and memory needs. If your users’ machines have a hard time maintaining a lot of Google Chrome tabs, they are going to struggle running your Electron app.
Step 4: Use Electron
Let’s clone a ‘getting started’ repo for our Electron project (side note: I’m a big believer in mono repos so you know the complete state of your project and stack at any given time, but this is your decision).
git clone firstname.lastname@example.org:EpsilonData/electron-getting-started.git;
Let’s get the code running:
The repo contains a
src directory and within this you’ll see a
dist-frontend directory and
dist-frontend is the code of your progressive web app that you’re packaging up with Electron. I checked this directory into source control so you could get started, but I encourage you to
.gitignore this directory and as part of your build process, copy your PWA’s Webpack
dist directory to here. As long as it contains Webpack’s generated
index.html and the rest, everything will be fine.
index.js is where the magic happens. Let’s take a dive.
You’ll see Electron getting required:
const electron = require('electron')
You’ll see some basic configuration like window size:
const windowSize = [420, 768]
const windowResizable = false
const openDevTools = false
mainWindow = new electron.BrowserWindow(
And you’ll see the line that loads your web app:
Experiment with the rest of the code in
index.js — you’ll see some ‘media key’ handling which comes from Euphoria itself. Later we’ll write about how to communicate between Electron and the PWA within it — media key control is a good example.
I’ve included an npm build script too which should give you some mileage towards packaging your Electron app up for distribution:
npm run build
Take a look in the
dist directory now — it’s full of goodies.
electron-getting-started.app is a real OS X application you can open like this:
electron-getting-started.exe contains a real Windows application you can run. And that’s it!
Euphoria as a PWA and Electron app
Euphoria is a PWA app that works across all modern browsers as well as on Android and iOS. Our optimisations and tweaks to create a native feel on iOS have been minimal — the best is yet to come. With some love and care, you could get an Electron-packaged PWA that looks like this:
- Progressive Web Apps are awesome — let’s make more
- Webpack outputs a
distdirectory which is perfect for Electron
- Electron does the heavy lifting of making “native” apps, but watch out for performance and accessibility
- Providing a progressive web app and a native app is the way to your users’ hearts ❤