One small suggestion I have is to load React from a CDN, and put it in a separate bundle otherwise (as a fallback). React is very popular, and loading it from a CDN means that it's more likely to be cached - if your users have already hit another site that used the same version of React, from the same CDN.
You're likely not upgrading your React version as often as you're upgrading your other vendor packages, which is why React should be separate. Otherwise, whenever you change any of the other vendor packages, users will need to download the whole of React again.
Great suggestion, thank you! The advantages are clear, but what about the disadvantages? Do you know any? I wonder why this is not more popular on apps using React.
Won't the other vendor packages expect react as a peer dependency? For example react routerdom
You mean the common bundle? It does depend on vendor bundle (which contains React and reactdom), which is why on the html we import the vendor bundle before the common
And what about Server Side rendering then? While doing SSR, I need react's renderToString function on server so I have to install react anyways. I am curious to know how did you do it?
I don’t have Node.js on the server side, so SSR is very hard to me. So I’m planning to use puppeteer to do prerender for crawlers.
Great write-up, Guilherme! Love that the diffs for each improvement are publicly available for folks to learn from. Did you use any other tools while auditing your app? (e.g Lighthouse) 😀
I did run it sometimes, but haven't stored it. I'll boot up both versions, compare the lighthouse result and post here later today! :)
This is client-side rendered, so there's still room to improve.
I wonder why you haven't mentioned using brotli in addition to gzip.
It produces far better compression ratio and decompression speed is off the hook.
It's supported by all major browsers (See caniuse.com/#feat=brotli) and has a webpack plugin (See github.com/mynameiswhm/brotli-webp...).
Hi Omer, I haven't mentioned that because we didn't implement it yet. But we're definitely interested on that too! 😀 Are you using Brotli already? Have you seen much difference?
I have used brotli in the past.
It's such a huge boost and it's super easy to implement.
I believe tree shaking is actually disabled in webpack if it finds you are using dynamic imports as it doesn't know (or doesn't go to the trouble to know) all the parts of a module different parts of your code will need.
With your react-icons usage you are imply importing just what you need. However, it appears that with v3 you will still have to import the complete icon set as the icons can no longer be imported individually (they come in a single file).
We're not using dynamic imports on react-icons, so Webpack can still tree shake it. Our react-icons is already on v3 and our bundles only have the SVG for the icons we use. Have you had a different outcome with this setup?
interesting, maybe there are situations where it will work and I read too much into this.. github.com/webpack/webpack/issues/...
I was definitely having issues and I guess I assumed this applied across the board (especially as we're doing code splitting on routes in the main component).
Amazing article on reducing bundle size from a broad range of perspectives.
There is only one mistake in this article - react-loadable.
It's unmaintained for a long time already, and should not be used at all (github.com/jamiebuilds/react-loada...). It's better to say - it always was unmaintained.
Lazy and Suspense are also not quite "done" yet, so please consider "loadable" - github.com/smooth-code/loadable-co...
I wasn't aware of that package, thanks for sharing. I've add a note on the post so others can have a look a that too.
You can use archer-svgs to load svg async and cache it in localStorage, when you reuse svg without http request!Remove svgs from your js-bunilde and Thin your js-bundle forever!(eg: Dont reload 100kb svg bundle only for 1kb svg update!)
Awesome practical tips,thanks!
I think that the other way to stop changing your main bundle whenever some other bundle changes is to create a runtime chunk separately. This chunk will contain all runtime info that was inside main bundle till yet. I have also written about some techniques to fine-tune your bundle size using webpack medium.com/@poshakajay/heres-how-i...
This is a great article, thanks for sharing too. I particularly like the bundle analysis idea I've been doing that lately, really helps prevent shipping large builds.
I've seen a wepback plugin that lazily loads css. I'm not sure how that works or whether that's possible. But one of my colleagues showed it to me. Do youveyany experience with that?
We only lazy load the css from the page splitting. But our main css bundle is always loaded synchronously. My understanding is that when lazy loading the whole css, if there’s a delay to download it, the user will first see an ugly and broken layout
thanks for your thorough summary :). Now I learn a new tool bundlephobia.
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.