skip to content
Tushar's Avatar Tushar Tripathi

Making faster SPAs

/ 4 min read

I love SPAs. They are easy to build, easy to deploy and easy to serve at scale. They are also easy to make slow but I don’t think there is a better way to build dashboard and non trivial web applications. SSR and RSCs are not the right answer, the ceiling of how fast you can make them feel post initial load is much lower. A list of some things which can be done to get the best out of SPAs -

  • CDN and compression - Make sure you’re using a CDN to serve your static assets. This should be easy to setup with any cloud provider. If you’re using something like Vercel or Netlify, this is already taken care of. Do make sure the files are also compressed with brotli or at minimum gzip.
  • Caching - If you’re using modern build tools and any hosting provider this should be taken care of. Else, you can ensure that file hashes are part of the generated build file names and caching headers are set correctly to cache those files for a long time. This would ensure that the browser doesn’t need to fetch the same file again.
  • Code splitting - This again is taken care of by bundling tools like Rollup and Webpack. You can either configure them manually for more control or use framework provided features. For e.g. React.lazy combined with Suspense to show a fallback.
  • HTTP/2 - HTTP/2 allows for multiplexing of requests. In simple words if you’re making multiple calls to the same server, it would be much faster with HTTP/2. This is useful for both the backend and the frontend servers. Your hosting provider is either already doing it or you can configure it manually. There are config options in case you’re using a reverse proxy like Nginx or HAProxy.
  • Prerendering - For e.g. if your dashboard has a login page, it’s probably going to be the same for all users and can be prerendered. You can also just render the first navigation on server but the increased complexity is usually not worth it. Other techniques like proper caching and service workers will be much more effective.
  • API calls Waterfall - Pay attention to any kind of API waterfalls, to keep the initial bootstrapping of the app fast. The requests should happen in parallel, if there is a dependency, the api calls can be refactored and combined.
  • Service worker - Service workers let you cache network requests. You can for e.g. cache the entire application and all the assets required for rendering. The coolest part however is that you can give the cached assets when the tab loads, and update the asseets in the background if there has since been an update. You can thnen show the user a prompt to update the app to the latest version. This makes any subsequent navigation or reloads very fast even if the app is updated. You can do it using a library like Workbox or write your own custom solution.
  • Edge Proxy servers - So this is more for the API calls than the SPA itself(which is served with CDNs). The things is that if you’re sending requests on public internet, it won’t always take the most optimal route. For e.g. if your servers are in US and a user in India is trying to access them, you can reduce the network RTT required by almost ~50% with optimized network routes. AWS even has a page to test the improvements possible. GCP also has an equivalent feature they call premium networking. Closer servers mean quicker responses.
  • Offline first - This is a bit more involved, but can really improve the user experience. The idea is to use strategies like serving stale data with background fetch, optimistic updates, CRDTs, conflict resolution etc. to make the application work offline. This is especially useful for mobile users who might have spotty network connections. You can use a library like Replicache or ClientDB to make this easier.
  • Edge Cache - You can cache a subset of commonly fetched data on the edge proxy servers, further reducing the network time. This is especially useful when the data required is shared between a group of users. For e.g. in a Slack workspace, it’s likely that there will be spatial locality in users part of the same workspace. These users will need very similar data around channels, messages, other memebers etc. Caching this data on the edge servers with consistent hash based routing can really speed up the application. In fact, Slack already does this with Flannel.