Building Progressive Web Apps with React and Gatsby

We are in the middle of a new era of web interactivity and the phrase on everyone's mind is Progressive Web Apps (PWAs). A PWA is a web experience that has many of the same features and benefits previously reserved for native applications. Native features such as offline readiness, push notifications, Bluetooth, filesystem integration and more are now possible with rich new platform APIs. PWAs bridge the gap between the web, with its ubiquity and reach; and native apps, with their system integration and capability.

At this point you might be nodding along and thinking, "yes yes sounds great, but I'm not building an app. I'm building a website". Well, we're here to tell you that almost everything that makes a PWA wonderful can be easily applied to 'normal' websites to make them more engaging, performant and resilient, especially for mobile users. This is especially true with modern website frameworks like Gatsby.

What makes something a PWA?

A Progressive Web App can be defined as any web experience that is capable, reliable and installable. We'll see in a moment why that last point doesn't apply fully to our Gatsby websites, but everything else is crucial.

Capable

Until relatively recently the web was a restricted medium. JavaScript and servers gave us interactivity, but system-level capabilities like access to the filesystem, geolocation, push notifications, offline readiness and advanced computing lay in the domain of native apps. Now though, new APIs and standards are making these things possible on the web. We have service workers for offline capabilities, Web Assembly for heavy computing in C and other previously native-only languages and dozens system APIs in the browser for PWAs to take advantage of.

Reliable

With more and more of web usage moving to mobile devices, our apps have to be resilient and performant. Mobile devices can suddenly drop a network, get patchy reception and are underpowered compared to their desktop counterparts. Performance, both perceived and actual, is crucial for getting users to engage with your app or website — Google has found that as load times increase from 1s to 5s, bounce rates increase by 90%. Beyond performance, our web experience can't be affected by sudden network problems and should ideally be even available offline.

And since the web is a ubiquitous platform, our apps should gracefully degrade for users who don't have access to these new standards and APIs.

Installable

This is where differences between true PWAs and a PWA-enhanced website appear. Using Web Manifests we can instruct browsers to allow users to install our web apps directly on their device. Once installed it opens in its own window and has access to new capabilities a browser tab doesn't.

Generally, there isn't much sense in asking a user to install your website. But the metadata contained in a web manifest can still be useful for enhancing the experience in the browser, even without installation.

Getting progressive with Gatsby

By building a serverless static website with Gatsby, you are already most of the way to a rich, PWA-like experience for users. Gatsby pre-renders your site in a build step for incredible performance, even for the slowest mobile connections. Then by re-hydrating React on the client, users get an app-like browsing experience and great perceived performance, without refreshes between pages.

Gatsby also does a lot of optimisation for us out of the box. Code bundles are split and routes are lazy-loaded, important assets are pre-fetched and critical CSS is inlined for faster initial renders. But there's still things we can do to get even closer to a true PWA.

Optimising performance

Deploy to a CDN

Gatsby being a completely static site is a huge performance win on its own. But one of the real benefits of this is it means you can deploy your whole site directly to blazing-fast static file CDNs, rather than a traditional server. We personally recommend Netlify, which has a class-leading network and a lovely continuous build workflow for popular code repositories.

Use HTTP/2

HTTP/2 is a new standard for the web's basic communication protocol. It allows multiple requests to be served simultaneously (which makes the old advice of "bundle everything into as few requests as possible" redundant) and lets you specify critical assets that should be 'pushed' alongside your initial request, so they're ready before you even ask for them. It can drastically improve the performance of a complex site.

Most CDNs already serve content over HTTP/2. And if you're using Netlify, there's a handy Gatsby plugin (gatsby-plugin-netlify) that adds preload and HTTP/2 push headers for Gatsby's core bundles for you.

Switch to Preact

Gatsby is built on React, which is heavy with optimisation for app runtimes. Since our websites generally don't take advantage of these runtime optimisations, we can switch to the featherweight alternative, Preact. It weighs just 3kb and optimises for code weight and initial page load.

Switching to Preact is as simple as installing and including gatsby-plugin-preact in your Gastby config.

Lazy load resources

While Gatsby does a good job of code splitting our routes for us, you should also try and defer the loading of any large individual resources that aren't needed immediately. If you have a conditional module in your site (eg: a polyfill), use Webpack's dynamic imports to only load if and when it's needed. And if you are including external scripts, make sure they're marked as either async or defer so they don't block rendering.

Use resource hints

If you are relying on any external services (eg: Google Analytics, Facebook, Sentry, Google Fonts), setup resource hints like prefetching and preconnects for them.

Use gatsby-plugin-preconnect to easily add preconnect hints for external URLs. And add and tags to your site container with gatsby-plugin-react-helmet.

Use a global layout

Speaking of your site container, you should use gatsby-plugin-layout to create a global container for all your pages. This will allow you to build up a persistent 'shell' for your site between page loads and further optimise perceptive performance by doing things like transitioning active menu items and pages.

Going offline

One of the key technologies in progressive web apps are service workers. They are scripts that the browser runs in the background, separate from your site. And they allow us to do some really powerful things, like persist our full interactive site when offline, have periodic background syncing and even send push notifications.

They can be complex to setup, but luckily gatsby-plugin-offline does all the hard work for us. Just add it to the plugins array of your Gatsby config, or configure it with the additional options in its documentation.

Adding a manifest

Even though we (usually) don't want our Gatsby site to be installable by a user, a web app manifest can still add a lot of value. It can instruct a browser what icons to use across platforms, what colour tabs on supported mobile devices should be and more.

Install gatsby-plugin-manifest and include it in your Gatsby config with the options outlined in the documentation.

module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `GatsbyJS`,
        short_name: `GatsbyJS`,
        start_url: `/`,
        background_color: `#f7f0eb`,
        theme_color: `#a2466c`,
        display: `minimal-ui`,
        icon: `src/images/icon.png`,
      },
    },
  ],
};

Note that if you are using gatsby-plugin-offline as well, it must be included after gatsby-plugin-manifest in your Gatsby config, to ensure the manifest is registered in your service worker.

Wrapping up

With all this optimisation done, it's time to see if your hard work has paid off. We recommend using Google's built-in Lighthouse auditing tool to test your shiny new progressive Gatsby site. Open your site in Google Chrome (ideally in an incognito window, so any extensions you have don't interfere with testing), the open inspector and head to the Audits tab. Run an audit and see how you score!

Keep in mind that Lighthouse is notoriously picky, especially about performance. Generally though if you score 10/10 on PWA metrics and 90+ on performance, best practices and accessibility, you have a gold-standard PWA-like Gatsby powered website.

Tell us about your project

Get in touch