Progressive Web AMP: PWA + AMP using Meteor

Quick Summary: We created a Progressive Web AMP (PWAMP) using Meteor. Very fast first delivery + rich and engaging app features

Previously, I have posted how we made the first load of our web app fast on this topic: Fast initial load of dynamic pages with Meteor and React

But the public pagespeed / lighthouse score is still lower than the 90+ goal that we have. After a few research, I realized that AMP has the capability to warm up a service worker in the background to launch a PWA. So basically, we created an AMP version for all dynamic public pages that we have that can be cached by Google. Prior to the caching, we are already hitting 80+ (90+ on some) on all pages in the public PageSpeed site. I think it will hit 90+ in all pages once cached by Google.

To create AMP, we did the following:

  1. Inline all critical CSS using the technique already mentioned here: Fast initial load of dynamic pages with Meteor and React

  2. We are lazy loading most of our images so we just changed it to use <amp-img> tags when in AMP mode

  3. Hide all functionality not required in AMP or link them to the corresponding functionality in PWA. This includes removing the CSS and JS bundle generated by Meteor.

  4. Catch the HTML output of Meteor and convert them to AMP by passing it through the AMP optimizer which adds all the necessary boilerplate required for AMP. Depending on the AMP features used, it also automatically adds the required packages (I don’t even know what those packages are until now :sweat_smile:)


  1. Our PWA has an existing service worker using WorkBox (so easy to use)

  2. To warm up the PWA during the first access of the AMP page, there is a need to do precaching also through workbox

  3. During meteor build our build script updates the service worker to include the generated CSS and JS bundles in the precached urls for #2

  4. In all AMP pages, we include the amp-install-serviceworker that warms up the service worker, which in turn precaches the css and js bundles, which in effect preloads the PWA in the background. Any action on the AMP page loads the PWA

Example cached AMP page:


Nice work, great list of functionality!

What do you like best about the AMP implementation, would your recommend it to others? Have you run into any AMP limitations with what you can build?

Definitely, the best thing about AMp is the fast speed of first delivery. Here is one of the first pages cached by google (also updated the original post)

I recommend this if the app can benefit from a lot of SEO traffic e.g. a lot of public pages that can be an entry point to a user. This ensures instant loading of first page for users coming from search engines (google, for example, preloads the page when viewing the search results resulting to that instant experience)

The biggest challenge was converting from html output to amp. We ended up with the forked webapp package. AMP requires zero validation error and the amp optimizer helps a lot. We can opt not to fork webapp package but we have to manually create the amp page

Amp has its own set of tags. We only focused on the image replacement <amp-img> and created a component for this. It displays normal lazy loaded images in PWA and amp-img in amp. The rest of JS functionality are either url-based or are hidden in the amp version. This does not stop us from adding any features or functionalities in the PWA version (we just created the function isAmp() to tell the renderer to hide the element or not)

When we reviewed the requirements on creating a PWAMP, we ended up with this list

  1. Inline CSS
  2. SSR
  3. service worker / PWA
  4. AMP

We already have the first three before we started so all we have to focus with is converting our SSR html to SSR amp


AMP pages are pretty fast. Way faster than the fastest we can achieve with our PWA page

Here is the actual data collected by search console

All our AMP pages are scored as 90+ in the public PageSpeed site.

1 Like