Top 8 Tips for Optimizing React Apps for Hyper-Fast Load Times

There’s nothing as delightful to a user as a web page that loads up in the blink of an eye. I know this from my experience using React, JavaScript and Node.js to build speedy apps. However, achieving AND maintaining an app with this characteristic is not easy. So here are the top tips you can use to optimize your React application so that it loads up hyper fast in a browser and doesn’t require lots of RAM/computing power to keep running. Note: I’ll state what you need to do on a high level and then link to tutorials/articles that will give you details. Also, the tips below are arranged in the approximate order in which you should implement them. However, they are all important (though some are crucial). So make sure you read and use them all!

(NB: the version of webpack referenced in this article is webpack 4.)

Steps for Optimizing your React App for Speedy Load Times

1. Load static and dynamic JS code separately

The JavaScript code of your project can be grouped into two broad classes: your project’s source code and the code imported from your dependencies (inside node_modules). Of these two, your source code will be dynamic, while the code from your app’s dependencies will be more or less static. Find a way to supply these two classes of code as separate <script> tags to the browser. This will enable the browser to cache the code for dependencies for a longer time while reloading your source code as often as necessary. Achieving this will be far easier if you use webpack: just update your webpack.config.js to output two files that are conventionally named bundle.js (your project’s code) and vendor.js (your dependencies).

This technique is super useful because over 90% of webpack’s output is usually code from your dependencies. Now, let’s say webpack normally outputs a 1 MB JS file. Applying this technique will cause the browser to reload only 100 kB of data when you change just your source code, instead of the browser reloading 1 MB. That’s a 90% improvement in performance, so go for it! You can read this article for details on how to split your code into vendor.js and bundle.js.

2. Dedupe the packages/package versions in your vendor.js

Having split your app’s code into bundle.js and vendor.js, proceed to remove duplicate or equivalent packages from your vendor.js. Let’s say you use lodash. Your vendor.js may end up containing versions 1.2.0 and 1.2.1 of lodash after webpack is done bundling. However, because of semantic versioning it is highly unlikely that there will be significant differences between those two versions. So, you should modify your webpack config so that the more recent 1.2.1 will be used anywhere lodash is used in your app. However, exercise caution if the versions are significantly different- especially if they differ in major version e.g one is 2.0.0 and the other is 3.1.1. To try out this optimization, use this package to analyze your vendor.js for duplicates and then use this section of the package’s documentation to use webpack’s resolve.alias config to remove the duplicates you found.

3. Minify your CSS and JS files

For details on what it means to minify a file, see this article. You can save a lot (if not a ton) on file size when you minify your CSS and JS files. You can do this by manually running them through some minification software before serving them to the browser. Better still, if you use webpack, you can add plugins to webpack that will automatically minify your CSS and JS files for you. These plugins include terser-webpack-plugin, optimize-css-assets-webpack-plugin etc. Also, check out this excellent article for details on how to set them up using your webpack.config.js.

4. Load crucial JS/CSS first, load others afterwards

You may have JS/CSS files that are crucial for starting and running your app (e.g bundle.js) while there may be others that are not that important (e.g a script for adding ads called ads.js). Next, make the browser prioritize loading the crucial stuff first and then load the non-crucial stuff when it can. One way of achieving this is to use <link rel="preload"> to load crucial stuff and <link rel="prefetch"> to load other stuff that are not crucial but which your app still needs. Read this article for further details on how to use <link> in this way.

5. Serve your files and API responses using brotli/gzip

You’ve split your React app’s JS code, deduped packages, minified your files and preloaded/prefetched them as necessary. Next, you should configure your server to use either brotli or, at least, gzip to compress the responses it sends. Brotli and gzip are both compression algorithms/formats and can almost certainly will help to greatly reduce the size of the files, API responses etc sent by your server to the browser. (Brotli is a recent algorithm from Google Inc. that usually gives better compression ratios while gzip is a little bit less compressing but is still great and is more backwards-compatible with browsers as it has been around for so long.)

However, you should use this compression in two ways: dynamic compression wherein you compress things on the fly (e.g API responses) and static compression wherein you compress things before the server starts running and then serve those things repeatedly (e.g JS files, CSS files etc). This is necessary because even though dynamic compression is very useful, it is wasteful to keep on compressing the same response over and over even though that response hasn’t changed. If you use express.js and/or webpack, you can find out how to implement dynamic and static compression using this fantastic article.

6. Use WebP images

Most modern browsers support images of the WebP format– a format developed for the web by Google Inc. and which gives consistently better compression-to-resolution or compression-to-quality ratios for JPG or PNG files. So instead of simply serving your image files as good old .jpg or .png files, consider serving them as .webp files. If you use an image hosting service like Cloudinary, check their documentation as they may already have an easy way for you to start using WebP images. In Cloudinary’s case, you can request for WebP versions of your uploaded images using the “format: auto” transformation. You can read more about this feature of Cloudinary in this article.

7. Avoid using multiple render-blocking resources (e.g stylesheets)

Stylesheets are render-blocking resources. That is, a browser won’t display a page until it is done loading the stylesheets it was given and using those to construct a CSSOM (CSS Object Model). Thus, avoid using multiple stylesheets- I’d say use just one. That will save the user bandwidth, processing power (of loading multiple resources) and load time. Then, keep that single stylesheet lean and minified and get it to the browser as fast as possible. You can read more about this optimization on this page. Use this same approach for other render-blocking resources too, like HTML imports, <script> tags not marked with async, fonts etc. I’ll talk about fonts below, but you can read some more about render-blocking resources and how to manage them on this page.

8. Use default fonts while your desired font is being loaded

Fonts are also render-blocking resources. So if you use Lato, Open Sans, Roboto Condensed or other custom fonts for your app and your CSS stipulates that body: { font-family: "Open Sans"; }, the browser won’t display your app until it is done downloading that custom font. This can leave your users looking at a blank screen for some precious seconds. (And if the font fails to download- the thought is very frightening!) To avoid this, stipulate body: { font-family: "Open Sans", sans-serif; } and then use the font-display CSS rule to tell the browser what to do once it finishes loading the custom font (swap it in immediately, don’t swap it in if the font took too long to load etc). Personally, I will advise you to use font-display: swap; or font-display: fallback; for your custom fonts. Furthermore, IF you’re using Google Fonts, you can modify the font’s <link> as follows:


<link href=”https://fonts.googleapis.com/css?family=Open+Sans&display=swap” rel=”stylesheet”>

You can read more about font-display and its swap policies on this page and this other page.

Honorable Mention: Server-Side Rendering/Universal Apps

You can also enable server-side rendering (SSR, or universal apps) in your React app to further speed up its load times. In simple terms, you will get React to render the first view/page/state of your app on the SERVER, not the client. However, SSR is not a win-win or 100% guaranteed optimization as, for one, the complexity of maintaining it for large apps can become very hard very fast- and I want you to only build React apps that are scalable and easy to maintain, whether the apps are large or small. For this and other reasons, server-side rendering is beyond the scope of my post today, but do let me know in the comments if you want me to write about it and I will do so if enough people ask me to. In the mean time, you can read more about it on this page.

Conclusion

There you have it. Those are my top tips for optimizing your React apps so that they load up super-fast. You should use them for your new apps and also add them to your existing apps, as there’s no better time to optimize an existing app than NOW.

Did I miss any obvious optimization(s)? Let me know in the comments below. Also, feel free to share your questions, comments, feedback and/or requests in the comments section below or by tweeting me and I will do my best to respond. Be sure to check out and share my other posts too.

Bedankt voor het lezen!

Drop a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.