Video Summary and Transcription
Quick React is a tool that speeds up React applications with less JavaScript, and Builder.io is a visual CMS that empowers marketing teams. Web performance is a challenge, with most websites scoring poorly. Island architecture and reasonability are alternative approaches to hydration that improve performance. QUIC allows for resumable applications and Quick React enables island architecture for faster startup times. Hydration and inter-island communication are crucial for interactivity in React applications.
1. Introduction to Quick React
Let's talk about Quick React and how it can speed up your React application with less JavaScript. Builder.io is a headless visual CMS that allows you to register existing React components and empower your marketing team with a visual editor. Builder.io also offers Partytown, which runs third-party code in web workers, and Mitosis, which generates idiomatic code for different frameworks.
Let's talk about Quick React, and specifically how to speed up your React application with less JavaScript.
Hi, my name is Misko Hevery, I'm a CTO at Builder.io and I've built these previous projects called Angular, and today I'm working on a project called Quick, and I want to specifically show you how Quick can be used to make React applications into smaller islands and load faster.
So I work for Builder.io, Builder.io is a headless visual CMS. What it means is that you can take your existing React application and do npm install builder into it, and once you install builder, you can register existing React components in your applications that you have, and that allows your marketing team to have a visual editor and they can drag and drop your components onto the page, and from the components they can change things, schedule when things go live, publish and send the data to the website. So you don't have to, as an engineer, be involved in every single small change.
We have an awesome team of open source contributors. First of all, Builder.io loves open source, so in addition to Quick, we have Partytown, which provides a way to run third party code, such as Google Analytics, inside of the web worker, which frees up the main thread to handle the application, makes the application more responsive. We also have Mitosis, which allows you to write your components in one style, and then those components get translated into all of the existing frameworks. So if you have a company where there are many teams using different frameworks and you want to have common design systems across everybody, Mitosis is a way to do it. The thing to do to understand about Mitosis is it's not a wrapper, over existing web components or anything like that. It actually generates idiomatic code for all different frameworks. And of course, we have these three awesome gentlemen who are working on the open source, Adam Bradley, who's creator of Ionic, Manu Martinez, Almeda, who also worked on Ionic and GEN, and finally, Sam Jaber, who's working on Mitosis.
2. Challenges with Web Performance
Our web is awesome, but it's not as fast as it should be. Google scores websites based on user experience, and most websites score in the red. It's difficult to achieve good performance.
Now, the thing I want to talk to you about is that our web is awesome, but it's not as fast as it should be. And Google cares about this, and they see that the users leave sites that don't perform as well. And so Google scores websites, and they have something called Core web vitals. And this is actually recorded on what kind of a user experience people have when they use the Chrome browser. And as you can see, that most websites actually get scored in the red. Only the websites that spend the most amount of time and energy can get yellow, and almost no website, actually, that I've seen in production that have real traffic are managing to get green. So what is going on? Why is it so difficult to get good performance?
3. Enhancing Web Interactivity and Performance
The applications of today are more interactive and rich than they were 10-20 years ago. Frameworks that perform the best in terms of Lighthouse core also ship the least amount of code. However, we still need to ship code to make the application interactive. Server-side prerendering may appear faster, but it still requires downloading, executing, and rendering the application. The interactivity is actually slower because of duplicate amounts of information being sent. Hydration starts at a root component and recursively executes components, learning about listeners, state, and component boundaries.
And the reason for this increase is because the applications of today are a lot more interactive, lot more rich than they were 10, 20 years ago, and so we would expect that this particular trend would continue.
Now, unsurprisingly, I've picked a couple of frameworks on the left and show you what is their average performance that Google sees on the website. And on the right hand side, I'm showing you how much code these frameworks are shipping to the client. And notice that the frameworks that perform the best in terms of Lighthouse core also shipped the least amount of code. Again, this shouldn't be surprising because it basically tells you that, hey, the less code you ship, the easier it is for the browser to interpret it, execute it, and make the website interactive.
Now, the problem is that we need to ship code. We need to ship code in order to make the application interactive, and so we do this through this process called hydration. And the way hydration works is it starts with HTML, which essentially is a blank page first. And we'll talk about server-side rendering in a second. The JavaScript loads, the JavaScript then executes, then causes a rendering, and at this point, you actually have an actual interactive website. And it is at this point where you can go and click on it and perform operations.
Now, the problem is that we have this blank page at the very, very beginning, and so people started to do server-side prerendering. And so with server-side perendering, or SSR, or SSG, you end up with HTML, and the HTML now has a image that you can go and look at, but you can't really interact with it, right? And so there is an appearance of it being faster because the page actually appears faster for the user, and so that provides a better user experience. But it still has to download all of the JavaScript, it still has to execute all of the application, and it still has to render the application, except now we don't call that rendering. Instead we call it reconciliation, because we're trying to reuse as many DOM elements as possible inside of the UI. And so it is only at this point that the application becomes interactive. And notice what's actually happening, is that the interactivity is actually slower, have you said no content at the beginning at all. And so it's a bit of a dichotomy that we appear faster, but we're actually slower. And the reason for that is because we're actually sending more information, we're sending duplicate amounts of information. If you think about it, if you take a string, like visually built on your tech stack that you see in the image, that string actually appears twice. Once in HTML and once in JavaScript. And so this duplication actually means that you have to send more stuff to the client, the client has to process more stuff. And the end result is that there is a... that it takes longer for the application to wake up. Now, hydration, if you think about it, what it is, is that you start at a root component. And as this new root component gets processed and executed, hydration learned, the framework learns about other components, in this case, these three. And so it recursively goes and executes these components and learns about more components. And as it does so, it learns about the listeners. And it's the listeners that we actually care about. Listeners, state, and the boundaries of the components is the information that we need in order to make the application interactive.
4. Island Architecture and Reasonability
Listeners, state, and the boundaries of the components are crucial for making the application interactive. Alternative approaches to hydration, such as Partial Hydration and React Server Components, break the application into islands, allowing for better performance and user experience. Reasonability, popularized by frameworks like QUIC, provides a middle ground by combining hydration and reasonability. Island architecture, exemplified by frameworks like Astro and Fresh, offers a way to achieve reasonability without changing the framework. Quick enables island architecture for React applications, providing faster startup times. Reasonability starts with HTML, containing the necessary information for the framework, making the page immediately interactive.
Listeners, state, and the boundaries of the components is the information that we need in order to make the application interactive. And so, because this takes a lot of time and all the application has to be present, people are looking for alternatives to hydration.
And so there are different variations on it. The one is called a Partial Hydration, in which case you turn everything into islands. And so the idea is that you recognize that this particular island is not interactive and therefore there's no need to download and execute it on the client. Whereas this island is super small and can be woken up so quickly that maybe you can do it just on click interaction, maybe this island is a little bigger. And so you have to do it on idle or on visible to get the best possible performance. But by breaking the application into islands, it allows you to not overwhelm the browser or at the beginning and what gives a better user experience.
Another form of this is React Server Components, which if you think about it, are kind of like islands except that instead of being treated as independent applications, they're actually treated as a one unified application, because some of the VDOM is computed on a server and just sent to the client and therefore the client doesn't have to re execute that portion of the component. And the only thing that the client has to execute are the components that are actually interactive. So we have kind of extremes, right. On one extreme, we have just basic hydration, which Angular and React and other frameworks do. And on the other extreme, we have this idea of reasonability. We'll talk about it a little more and frameworks like QUIC have popularized it, but QUIC by no means is the first one. Google has a framework called Wiz that has been doing reasonability for about the past 10 years. And Marco has a framework. Sorry, eBay has a framework called Marco which in the latest version 6, they also provide reasonability for your applications as well. And all that means that the applications essentially get to start up faster. But there is this middle ground which is basically islands and islands says, Hey, I can basically still do reasonability, which means I don't have to change my framework. I have to do still hydration, which means I don't have to do my change my framework and still do what I have done before. But instead of doing a hydration on the whole thing, as I said, we can do a smaller chunks. Now this has been popularized by frameworks such as Astro and currently a framework called Fresh. But what I want to show you is that you can also get island architecture for your React application with Quick. And so that's what we're going to talk about in a second.
Now, reasonability is a bit different than hydration in that it reasonably starts with HTML. You get a page, but the page already has all the information needed for the framework. Now, doesn't mean that it has all of the code of the application. It would be too large to serialize into the page, but it has information about where the listeners are and what code needs to be downloaded when you go and interact with one of these listeners. And so as a result, the page is immediately interactive. Now, not only does it appear faster, it actually is faster and you immediately start downloading the JavaScript that if the user goes and interacts, there will be no delay on interaction.
5. QUIC Installation and Component Building
QUIC allows you to resume the application where it left off, eliminating the need for re-execution or reconciliation. Install QUIC City and Quick React, then build a Hello World component and a counter in React. QUICify function turns the component into an island, allowing resumability with zero cost.
And notice that the amount of JavaScript you download is much much smaller. And that because you resume, you don't have to re-execute all the components. Most of these components can be removed because they're actually duplicate information. And so with resumability, you don't have the double-download problem that exists in Hydration. And as a result, there is no need to re-execute the application or do any sort of reconciliation. The application simply just continues where it left off.
So let me give you a demo. So let's start off by installing QUIC. So you do npm create QUIC at latest. This contains a meta-framework, which we called QUIC City. So let's install QUIC City in there as well, install the dependencies, git, and so on, and you're ready to go. The second step is to install Quick React. So this is a wrapper that allows QUIC to use React components. As you can see, we can install QUIC and add a React integration, go through all the steps, and set everything up. And now we're ready to go and build something in QUIC.
So let's build a Hello World to see what a Hello World looks like. On the right hand side, I have a React component that you can see. Notice at the top, there is a pragmat that says JSX import source react. This tells the compiler that hey, this is a React code, make sure you deal with JSX in a way that React likes it. And we create a basic component using the function greetings. Now the thing that QUIC adds is that it allows you to take the greetings component and run through a QUICify function to get a QUICify QUIC version of this particular component. Now this is a runtime wrapper, this is not a translation, so you have, you'll get the same exact behavior as before. You don't have to worry about compatibility or anything of that sort. But in practice, what it means, is that it turns the component into an island. So because QUIC is resumable, it essentially can start up with zero cost, but now your greetings component has cost that under some conditions, we, you will have to execute and hydrate. In this particular case, the greetings actually never hydrates because there's no signal to the, to QUIC, to tell it, hey, it has to be hydrated. And if you think about it, this makes perfect sense because in this case, greetings is indeed and therefore there's no need to do anything with it. Now let's do something more complicated. Let's build a counter. So let's build a counter in React and you can see, you have a button and you can click on it, which changes the state and updates the count.
6. Hydration and Inter-Island Communication
We create an island of React inside an existing application by quickifying the counter. Hydration is necessary to make the code interactive, and it can be triggered by specific conditions like hovering over a button. Inter-island communication is crucial, and we demonstrate it with two islands: a button and a display. The state transitions between the islands, and Quick owns this state. Lazy hydration allows components to hydrate based on changes in inputs or props. Different methods like hover, load, idle, visible, click, signals, and property changes can wake up the system.
And again, we're going to go and quickify the counter to get a component and then, you know, embed it inside of a larger application. So we create an island of React inside of an existing application. Now if you go and click on it, notice nothing happens. And that is because the code isn't hydrated. So you need to tell the system like, hey, under which conditions does this particular island have to be hydrated? And the way you do that is through this eagerness property. So in this particular case, we have the same piece of code. And now we're saying, hey, you know, when you hover over this particular button, which means you're very likely to about to click on it. By the way, there's other ways of doing it. Go ahead and hydrate the component. So if you look at what's happening is there is now React going on. When you hover, you can see that the React DevTools have been downloaded and they have initialized and we have downloaded the necessary code. And because it has initialized, you can see that the React counter has been rendered. And now that it's rendered and hydrated, you can go and interact with it and everything works exactly as you would expect.
Now, one of the important things about islands is that you need to solve the inter-island communication problems. These islands are effectively independent React applications and they need to be able to talk to each other. So what I'm going to show you here is that you have two islands, one is a button and another one is a display. In a button case, we are going to, again, quickify it by running it through a quickify function. And we're going to specify eagerness hover, which means that as soon as I hover over the particular button, I want the button to hydrate. In a case of a display, we're not specifying any sort of hydration at all. What happens on the client side is that we need to have a state that transitions from both the button and the display. In this state, because it is owned on a way that it's two separate islands, it really has to be owned by Quick. So Quick now has a state, and this state is passed both to a button, which is hydrated on hover, but also to the display. Notice what happens in here. When I go and hover over the button, you can see the DevTools come up and the React button is being rendered. But now when I go and I click on it, the actual act of clicking... First, there is just a button, and then if you go back and you click on it now, now you can see that, hey, the React display has hydrated and executed as well, and so now we have this lazy hydration situation where the button hydrates eagerly, but the display only hydrates when the inputs or the props of it change. And if you think about it, this is exactly how React works underneath it, but we're emulating the same exact thing with signals and islands as well.
Now, just to point out, you know, a hover is not the only way to wake up the system. You can do it on load, idle, visible, click our parameters, and when you wake up things, you can also wake things up using signals or property changes, as we have shown.
Comments