How we build web apps with class-leading performance
While building web apps is easy, building performant web apps can be difficult. All the integrations, marketing tools, analytic embeds and interactive Javascript nowadays can cause your page to slow down more than you realise.
When we build a web app, we continually test its performance systematically and objectively against the industry standard auditing tool — Google Lighthouse (available in Chrome's inspector).
These audits measure a range of factors by loading a page and throttling down resources to compute a score out of 100. A 100/100 score represents best-in-class performance and is incredibly difficult to achieve, but a goal we aim for with all our projects.
Here's a few things we consider when addressing performance...
Optimising for first load
One of the first things a user notices in how long an app takes to load. There are two parts to this: time to first render and time to interactive (TTI). The biggest factors impacting first render and TTI is:
- the amount of code (particularly JavaScript) that the browser has to download, parse, and execute; and
- the number of render-blocking processes that have to complete before a page loads.
If your web app is relatively simply and doesn't need to optimise heavy animations or interactions during runtime, you can aggressively optimise initial payloads instead by using Preact in place of React, which will gives a massive reduction (up to 97% last time we checked) in JavaScript weight.
You can also follow an "app shell" model for UIs, prioritising the render of your basic layout before loading in components, will improve first render and users' perception of performance. You can pair this idea with animated grey blocks, also called a Skeleton state.
You can also set up code splitting by route to further reduce code weight, only loading the code required for any one page when it is viewed. Additionally, you can lazy-load heavy assets like web fonts, swapping them in once a page has loaded, rather than blocking render.
The overall goal is to avoid reliance on heavy JavaScript libraries wherever possible.
Using the metal
Wherever possible, you should aim to leverage the web platform for a given feature. This will both allow you to reduce code weight and have native-like performance under the hood.
Using new web standards, and polyfilling for legacy browsers (CSS Variables, CSS Grid, etc) Balancing developer productivity with web standard, by avoiding patterns like CSS-in-JS that are nice for developers but negatively impact performance
Progressive Web Apps
New browser APIs allow you to prepare your app as a Progressive Web App that is offline-capable, installable on mobile devices, and highly performant. You can build service workers (or more likely, use a plugin like next-pwa) to intelligently cache content and harden your app against patchy network connectivity.
Additionally, you can look into prefetching, preloading, and preconnecting to critical resources, as well as setting up a solid manifest to make your web app installable on mobile devices.
Optimising infrastructure
If you choose to develop a decoupled web app (a static frontend that consumes an API), you can easily take advantage of blazing fast infrastructure. We use tools like Firebase a lot for this type of setup.
Hosting on the right platform can also help — choosing a host with a global static-file CDN like Netlify or Vercel can speed up content delivery. If you can manage it, serving all assets over HTTP/2 can allow for simultaneous connections that vastly improve network performance.
Lastly, consider aggressively cache long-term static assets and using HTTP/2 Push to preemptively serve critical resources, before your app even requests them.