Because Single-Page Applications are cursed with slow loading
In this article, we’re gonna explore different ways we can reduce the initial render latency of our single-page applications.
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
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
classnames has zero dependencies,
react-easy-swipe has only one dependency which is
prop-types is also the third dependency of
prop-types is a package you’ll end up using anyway, but if you still dig deeper,
prop-types has three dependencies;
react-is. React itself depends on all these dependencies except
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
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
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.