JavaScript on Demand: How Qwik Differs From React Hydration

In this post I’ll attempt to explain resumability, Qwik’s approach to solving the beastly client-side JavaScript problem. I’ll also explain how it differs from React.

There’s a really great introduction to Qwik over at QwikSchool.com, which covers some of the areas I’ll be outlining in this article. But for me, reading is how I learn best and I learn faster when I can see a relatable comparison, which is what I’ll attempt to do now using a bar and pints of beer.

How Does React’s Hydration Work?

Imagine you walk into a bar and sit down, but the server doesn’t ask you what you want to drink, they just pour six pints of beer into a giant glass. Once they’re satisfied that the glass can hold six pints of beer, they pour it away. They then pass the empty glass over to you (the client) and proceed to pour another six pints of beer into the glass.

You’ll be asked to pay for 12 pints, six of which were poured away and five of which you likely don’t need right away.

That’s how React’s hydration works!

How Does Qwik’s Resumability Work?

Imagine you walk into a bar and sit down, and the server asks you what you’d like to drink. You reply, “One pint of Ale please”. The server then proceeds to pour a single pint into a normal-sized glass and hands it to you.

You’ll be asked to pay for one pint. And that’s Qwik’s resumability in a nutshell (or beer glass, in this case).

At a very high level, you should see that with React there’s an awful lot of waste and not an overly accurate method of determining what you want or when you want it.

React’s Hydration Replay

Using the above example again, you’ll have noticed the server poured six pints of beer into a giant glass, then poured it away leaving only an empty glass which was handed to you (the client) and then refilled it.

When React renders on the server, this is what happens. The application is built server-side and then thrown away. The server will, however, send the HTML to the client, followed by a chunk of JavaScript which is used to effectively replay the same steps, only this time on the client (browser). If for any reason the replay is unsuccessful, you’ll see hydration errors in your console.

The server needs to build the page once, so it knows what it’ll be sending to the client — which kind of explains half of the hydration replay, but you might be left wondering why, in my beer analogy, were all 6 pints poured at once? I also wondered about this, and the answer lies in “route chunking.”

What Is Route Chunking?

This will slightly depend on which React framework you’re using, but when a React application is built on the server, the framework will figure out which JavaScript to include based on which route a user is on. For example, if they’re on a /dashboard route, they’ll need different JavaScript than if they’re on a /settings route. Depending on the route will determine which parts of your application’s JavaScript are included in the chunk. This is some pretty smart stuff, but it could be even smarter.

What Is Dynamic Component Chunking?

Qwik’s approach is much more granular. Rather than determining which JavaScript to include based on the route a user is on, it chunks the JavaScript into much, much smaller pieces. These smaller pieces can then be delivered to the client much faster; and more than that, Qwik is able to determine at which point during a user’s visit to any given route they might need these smaller chunks of JavaScript.

I Need a Dollar

Everything in Qwik uses the $ suffix. For example, this simple component is wrapped with component$().


Using the $ syntax, Qwik is more easily able to optimize the chunking boundaries — which results in smaller individual JavaScript chunks.

Not only that, it’s able to serve up these smaller chunks as and when they are required.

Take this onClick$, for instance. The JavaScript to make this work is only needed if a user actually clicks the button. If a user doesn’t click the button, the JavaScript required to display “Hello world!” in the console will only exist in the browser cache (which has been fetched by Qwik’s service worker), it won’t have been eagerly fetched and downloaded until it’s actually needed.

Scale this thinking up to a much larger application with many different areas of interactivity and ask yourself: do all users need all the JavaScript, all of the time? Or do some users only need some of the JavaScript, some of the time?

The Service Worker

This is where things get really interesting. You might be familiar with Partytown, an open source project from the makers of Qwik that allows you to offload client-side script loading for things such as Google Analytics to a service worker. By offloading non-essential scripts to a service worker, you free up the browser’s main thread, which can then be used to more efficiently load the application code that powers an application.

But…

By default, Qwik uses a service worker to load the actual application code! And maybe, in an alternate dimension, that would mean you could continue to load Google Analytics on the main thread with limited performance implications. 🤷

Once JavaScript has been loaded by the service worker, it’s cached by the browser, so next time a user does something that might reuse code that has already been downloaded, Qwik will load it from the cache, not over the network. It’s at this point my beer analogy goes a little haywire but… hopefully, you’ll get my point.

A Polite Example

To give you an example, on each of the “post” pages on my site, I have a Reactions component.

It looks like this.

Screenshot of paulie.dev reaction component

This component is responsible for the following:

But thanks to Qwik, the way it does this is pretty smart.

Allow me to explain. Here’s a screenshot of the network tab for the same post:

Screenshot of paulie.dev initial load of Qwik core in the network tab of a browser

So far there’s only ~19kb of JavaScript loaded into this page (That’s the core Qwik code).

Scroll down a bit further, when the Reaction component enters the viewport and Qwik loads the individual chunks of JavaScript required to display the smiley faces, and kicks off a client-side GET request for the totals.

Screnshot of paulie.dev lnetwork tab loading additional Qwik code when component enters the viewport

But wait, there’s more.

Interact with the Reactions component (which makes a POST request) and you’ll notice Qwik once again loads the individual chunks of JavaScript required to enable this functionality.

Screenshot of paulie.dev loading remainin Qwik code upon interaction in the network tab

Each of these chunks are really small; in my case, most are less than a single kilobyte (KB)!

Qwik Thinking

And that’s Qwik’s thinking. Only load the JavaScript that’s required; but more crucially, when it’s required. This is of particular importance for something like this Reactions component, which is right at the bottom of what is actually a “static page” — meaning there’s no server-side data fetching or rendering and will load faster than an SSR page, but it still needs to perform both GET and POST functionality.

In a lot of cases, readers of my posts won’t ever scroll to the bottom of the page, so there’s no need for any of this data — or the JavaScript to ever be loaded. But, when / if they do, Qwik steps in and intelligently decides what and when to load. It’s genius!

React Brain

It is possible, of course, to achieve the same thing using React (kind of). You could write your own intersection observer and combine it with React’s lazy/suspense so that data fetching (and the JavaScript required to enable interactivity) only occurs if the component is scrolled into the viewport; but, these are things that you as the developer need to think about and optimize for, whereas Qwik does all this thinking for you!

Final Thoughts

Whilst the bar and beer analogy might seem a little strange, I believe that in practical terms the theory holds up. You/the user only order and pay for what you want when you want it.

And quite honestly, why should it be any different with the application code? It makes no sense to pour half of it away and then over-deliver and over-charge without even asking. Really, what we all want is to simply pay for what we ordered.

There’s a technical explanation written by the Qwik team over in the docs, you should probably have a read of that too: Resumable vs. Hydration. But if you like the sound of Qwik, go check it out! I’ve had a blast learning Qwik and have been regularly surprised at how little I have to think about stuff in order to achieve absolutely cracking performance.

Group Created with Sketch.

 

 

 

 

Top