Tips for Reducing Initial Render Latency of Single-Page Applications | by Eric McWinNEr | Mar, 2022

Because Single-Page Applications are cursed with slow loading

Cars going fast on a road

Single-page applications built with modern Javascript frameworks such as React and Vue are the norm with building web applications today. In today’s age, where speed is paramount and everyone has never been more impatient, a few seconds of latency could be the difference between a new customer and a bad review on Twitter.

In this article, we’re gonna explore different ways we can reduce the initial render latency of our single-page applications.

Before we dive into the tips for reducing the initial render latency, we should take a couple of seconds to discuss why this is even worth bothering ourselves with in the first place. The whole point of Single-Page Applications (SPAs) is that they load your website’s entire JavaScript and CSS at once and then they show or remove parts of the page with JavaScript, resulting in a fast, seamless, modern experience.

You get just one HTML file, and the content of that file is managed by JavaScript, hence “Single Page.” How is this achieved? With module bundlers.

SPAs use module bundlers like Webpack, Rollup, Vite, etc to jampack all the JavaScript you write in your entire application (and all the code you need from node_modules) into one massive JavaScript file, and then jam-pack all the CSS you write into one massive CSS file.

It also keeps track of your other assets and manages these too. When a customer visits your website for the first time, your browser downloads the massive JS and CSS file and with that, your browser has all the information it needs to manage and manipulate your entire website via JavaScript.

The result is that the process of loading these necessary files the first time would be slow, but after the necessary files are downloaded, we get the seamless experience SPAs are known for.

Now that we know this, what can we do to reduce this inevitable latency when the browser loads the files our SPA needs to render initially?

As discussed above, SPAs by design are cursed to load slowly initially as they download the files they need. There are a bunch of tips we can use to reduce this time to the barest minimum and we’ll discuss them below:

1. Lazy Loading and Code-Splitting

Lazy loading is the process of loading parts of your application when you really need them, as opposed to loading the whole thing at once. Code-splitting is about instructing your module bundler to split the JavaScript into logical bundles, rather than jam-packing the whole JavaScript into one file. It will create different files that will be downloaded only when we perform actions on the app that need those files; they will be lazy-loaded.

All modern module bundlers come with code-splitting. When we do this, the module bundler splits the jam-packed files into logical sections so that the browser only downloads the parts it needs to render for the first time and downloads the code for other parts of the system when it needs it.

Most module bundlers powering single-paged applications recognize using the dynamic import syntax import(). A good question could be at what point do we split our applications? A good place to start will be at specific routes. All routes that don’t need to be accessed immediately at the first render can be split into a separate bundle.

For instance, we can separate our authentication pages from our dashboard pages, since the unauthenticated users won’t need any dashboard feature at initial render and they will have to wait for a login process anyway to access the dashboard pages. We could also chunk our dashboard routes, like all pages necessary for a similar action can be split into a separate bundle.

Because this article really isn’t about code-splitting, here’s some good reads to learn how to implement code-splitting in your Vue and React apps.

Code-Splitting using React.lazy and suspense

Code-Splitting at Routes with React Router

Code-Splitting in Vue3 using async Components

Lazy Loading Routes with Vue Router

2. Pay Attention to your Third-Party Dependencies

There are countless packages on npm, and there’s a package for every little thing you want to do. The thing to remember is these packages eventually make it into your bundle for everything to work. These packages usually have other packages they depend on, which in turn have other packages they depend on. It could go on and on, giving us a make-shift Droste Effect.

You get the gist?

Let’s be realistic though, using third-party plugins are inevitable. You’re already using a ton of third-party dependencies by virtue of building a Single-Page Application. The least we can do is pay attention to the packages we use. Take note of the dependencies they have and the dependencies of their dependencies.

The less the code baggage we import with our third-party plugins, the less code makes it into our bundle, and the less code our browser needs to download to get things running.

Here’s how you can check the dependencies of the next package you want to add to your project:

As seen in the picture above react-responsive-carousel has three dependencies. This really isn’t bad news though, clicking on that link shows us its three dependencies.

Three dependencies are small enough not to worry about and I’ll happily use this package in my application. However, if you’re like me who decided to dig deeper, let’s look at react-responsive-carousel‘s dependencies. classnames has zero dependencies, react-easy-swipe has only one dependency which is prop-types, prop-types is also the third dependency of react-responsive-carousel.

prop-types is a package you’ll end up using anyway, but if you still dig deeper, prop-types has three dependencies; loose-envify, object-assign and react-is. React itself depends on all these dependencies except react-is and react-is has zero dependencies. This shows you how packages can inter-depend on each other’s dependencies. Thankfully, module bundlers are able to install each of these dependencies once and track them.

Another thing to look out for is the unpacked size of the package. Scrolling down a bit further shows us react-responsive-carousel unpacked size which is 188KB. This is also small enough not to worry about. Your eyebrows should be going up if a package’s unpacked size is approaching the megabytes.

Still, this also depends on if you’ll be using the entire thing for the initial render. A package like bootstrap-vue has an unpacked size of 49MB as at this writing but we can use it since we’ll most likely use a small part of it and that is what will end up in our bundle.

Time for the quintillion-dollar question: Do I do this extensive investigative research for every package I add to my SPA? F*ck No! Jesus! 😂. It’s important to know all of this, but it really isn’t necessary to know every dependency of the package you want to install.

In general, I just look at the unpacked size, and the number of dependencies; then I check for other packages that do the same thing and make an informed decision based on the functionality and the size.

While the number of dependencies and unpacked size can be good criteria for choosing packages, also remember sometimes it isn’t the entire package that makes it into our bundle. Lots of third-party dependencies like lodash and chakra-ui Also let us install only the specific functionality we need to our apps, and we should leverage on that, rather than installing the entire thing.

If you take nothing from this section, remember to make a conscious effort to check if you can install less to make your app work.

3. Pay close attention to your images

Images play a huge role in websites. We can barely have any application without images in one way or another. Images have to be downloaded in order to properly render any page and there are some ways we can reduce the sizes of the images to download.

The first thing we need to do is resize images where possible. We need to pay attention to the resolution of our pictures. A lot of times for clarity, we use high-resolution pictures that are simply overkill for websites.

As a rule of thumb, try to keep image resolutions no larger than 2000px wide (or long). There are several tools like Pixresizer for Windows or the Preview on Mac we can use to resize images and reduce the resolution while keeping them reasonably crisp. The moment a picture is more than 500KB, we probably need to resize it.

The second thing we need to do is to prefer svgs to pngs where possible. Svgs are lossless file formats used for graphics. Always prefer svgs to pngs because they are smaller in size and they look crisp no matter how you resize them.

Using svgs could have a reasonable impact on how fast your SPA renders initially.

4. Manage Fonts Properly

Render blocking assets are files that your browser needs to wait for in order to render your page. It normally won’t paint anything on the screen until these assets have been downloaded. These are usually assets defined in the <head> of your HTML file such as JavaScript files, CSS files, and fonts.

SPA module bundlers take care of a lot of the work needed with JS files and CSS files and we’ve discussed what we need to do manually, but we haven’t talked about fonts.

When working with fonts, there are two main strategies that make the browser work properly with fonts. The first is the font-display: swap CSS property and value. When setting up @font-face always remember to set this attribute as it tells your browser to load a system font until it finishes loading the custom font. This allows the user to see something until the font loads up.

The second optimization we can do is to serve fonts locally from our server rather than from CDNs. The reason for this is that more often than not, font providers tend to add more @font-face rules then you’ll ever probably need. This can have a negative impact on your application’s render time, and some font providers also don’t add in the font-display: swap rule which could result in delays in the initial render if the font file is large or the user’s internet isn’t fast

In this article, we discussed several ways to reduce the time taken for Single-Paged Applications to render for the first time. We looked at why it takes longer for SPAs to render than traditional web apps and why we should bother ourselves with these optimizations.

Taking all the steps above would produce significant results in reducing how long it takes to render.

I hope you learned something new today, and I’ll catch you in my next article.

Leave a Comment