Case Study: the Google I/O Extended 2017 Kuala Lumpur Progressive Web App

Fast, Beautiful and Works Offline + Built with Polymer 2.0

The Google I/O Extended 2017 Kuala Lumpur Progressive Web App is built using Polymer 2.0 based on Polymer Starter Kit.

Try it now:

Hi Paul! (Photo by me)

Magic? It’s Firebase Realtime Database!

I/O Extended 2017 Kuala Lumpur is one of the Google Developer Group Kuala Lumpur’s biggest events, and the ticket usually sold out very very quickly (the ticket for this event sold out in less than one hour!). Hence, it’s impossible for me to rebuild and redeploy the website once the ticket is sold out.

The problem is, I can’t use Firebase Remote Config, since it’s only available on Android, iOS, C++ and Unity. Solution? Firebase Realtime Database! So I just set and change the parameter values on Firebase console, and I just created my own Remote Config! Then, just write some if else code in the webapp, or just use Polymer’s <dom-if>to show the correct content based on the parameters you set in your database.

The Agenda and My I/O page is built with Firebase too. The schedule data are stored in Firebase Realtime Database, means I can update the schedule without rebuilding the whole site. Also, there’s one very cool feature — “My I/O”. Since there will be multiple tracks during the event, the attendee can plan early and customize their schedule. The schedule will always be synced from your account across mobile and desktop all thanks to Firebase Database.

By the way, if you are using <firebase-auth> together with Polymer CLI + JS Minify, it will break polymerfire. Read more here:


To create this scrolling + resize effect, I actually just modify the App Layout’s Resize Title effect’s from font-size to width. This is a very simple method that allows me to create this smooth animation just a few minutes.

Offline Caching

To provide a better experience in offline and spotty network situations, Google I/O Extended 2017 Kuala Lumpur Progressive Web App uses service worker to provide offline caching for its app shell and the site contents. The web app will only cache the files once everything is loaded, this will improve the page load performance and it will not block anything during initial load.

To notify the user when there’s an updated version of service worker, we can display a toast message to the user so they will know they should refresh the page to see the latest content.

if (navigator.serviceWorker && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.onstatechange = function(e) {
if ( === 'redundant') {
// Show Toast Message

And always remember, you should not cache the service-worker.js. If you are using CloudFlare, then you can use Page Rules to exclude your service worker file from CloudFlare’s caching. For Firebase Hosting, just set the cache control header for your service worker file to no-cache in the Firebase Deployment Configuration file: firebase.json.

"hosting": {
"headers": [
"source": "**/service-worker.js",
"headers": [
"key": "Cache-Control",
"value": "private, max-age=0, no-cache"

To cache to data from Firebase Realtime Database, you can use IndexedDB or Polymer’s <app-indexeddb-mirror>. This will works for API or storage layer that does not support caching data for later use during user sessions that begin while the user is disconnected from the network.

You can learn more about Polymer’s App Storage here:


Most of the time, images really slow down the website. Even worst, when the internet connection is really slow, those images just not showing at all, leaving a big gray or white box as your user waits for images to download. To improve your user experience, there’s one ingenious method which is used by Facebook, Medium, and more — “blur up” technique for loading images.

Just resize and compress the original image to ~1% of its original resolution and convert the image to base64. And thanks to Polymer, there’s one awesome component to do the rest for you, which is <iron-image>.

<iron-image fade preload placeholder="data:image/jpg;base64,..." src="..."></iron-image>

By using the components above, you will able to create this fast and cool looking images + transition on your website without slowing down anything.

Once the image is fully loaded, the placeholder image will fade out and show the original image (remember to add the fade option)

<iron-image> according to Google. Oh wait.

Hosting and HTTP/2 Server Push

This Progressive Web App is hosted on Firebase Hosting, which comes with SSL and HTTP/2 + HTTP/2 Server Push for free! What I need to do is just to add the link header in the firebase.json config file.

"hosting": {
"headers": [
"source": "/",
"headers": [{
"key": "Link",
"value": "</js/app.js>;rel=preload;as=script,

Thanks to HTTP/2 and HTTP/2 server push, bundle in no longer necessary and this allows multiplexed downloads over a single connection, so that multiple small files can be downloaded more efficiently.

You can read more about HTTP/2 on Firebase Hosting here:

Bonus Tips: You can run a local HTTP/2 server by using this command: polymer serve -P h2. Super neat!

Auto Deploy to Firebase Hosting

To makes your life easier, you can use Travis CI to auto deploy your Polymer app to Firebase Hosting when you push your code to GitHub:

language: node_js
node_js: 7
- node_modules
- bower_components
- npm install -g bower polymer-cli firebase-tools
- bower install
- polymer build
- firebase deploy --token $FIREBASE_TOKEN --non-interactive --only hosting
Yeah, trust me, Travis really makes your life ‘easier’.

That’s All!

And this is how I created this Progressive Web App for I/O Extended 2017 Kuala Lumpur in less than one week using Polymer 2.0. Hope to see you all at the event.

Event Page:

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.