Table of Contents
5. HTML Streaming
He showed a Kroger app on the right that drew in boxes of content versus a version of the app on the left that loaded everything at once.
The page works by sending the HTML (including the placeholder) first and then, without closing the connection on the server as the page finishes, it adds the missing parts of the page to the end of the document, via a script tag. The script page basically swaps the content into place to replace the placeholders. React, Solid and eBay’s Marko.js support this approach, he added.
“The benefit of streaming, especially for larger services, is this ability to be able to decouple slow responses, unpredictable responses, so that the overall reliability of your system can be better,” Carniato said. “As I said, only a handful support it today, but luckily React is in that handful, which means that you can use streaming, Next, Remix and a lot of common frameworks.”
6. Islands — a.k.a, Partial Hydration
“So welcome to the tropics, more water — islands, also known as partial hydration, are not a new technique, but they didn’t really get popularized till more recently,” Carniato said.
The concept of this partial hydration was introduced in Marko.js at eBay back in 2014, but it’s been popularized since Astro and Fresh introduced it as “islands,” he said. Islands are an updated take on multi-page applications, he explained. The concept is simple, according to Carniato: A developer looks at the app as sections and renders the sections almost like separate apps acting independently.
The trade-off, however, is that it brings back full-page reloads.
For instance, if you’re fetching data on a server, you can have some kind of island wrapper, then the server renders a list into it, and each of those items will be in a client wrapper and have their content still be on a server, he said.
Carniato calls it double data when developers want data used on the server to hydrate in the client, which requires the whole thing to be serialized. The data essentially renders as HTML, then it renders again on the initial page as JSON, he explained.
“Partial hydration is great at reducing the amount of code that reached the browser, but what about that last 15, 20%?” he asked.
That last mile is where resumability comes in, Carniato said. Resumability is the ability of a framework to resume execution of an application where it left off on the server.
It works by serializing the application state and sending it to the client as part of the server-rendered HTML. When the client receives the HTML, it can deserialize the application state and resume execution of the application.
“Basically, we wouldn’t need to actually run anything except attach those event handlers, when the page loaded up,” he said. “At least in theory — it’s a little bit more complicated than that. But you can take a snapshot, attach a few event handlers, and then basically the first time you click on something on the page, it basically knows where it left off and can just apply it.”
There is a cost to serialization, he warned, but the execution cost is almost zero and there’s basically zero scaling cost.
“That is a very interesting way to attack that last bit of what’s left, and as it turns out, breaking up the code where the entries are event handlers, conceptually, lets us identify [and] tree shake out even more code, in some cases [more] than islands,” he added.
But there are things that developers want to persist on the page, and being an application means more than having client-side animations, he said; that’s where hybrid routing comes into play.
8. Hybrid Routing
“What we need to do here is [to] kind of diff the content coming in so we can preserve our stateful client islands,” Carniato said. “Just because we get new content doesn’t mean we should lose the current state. That could be stateful data, it could be the focus of an input on your page, one on one.”
The tricky thing is, if you’re not, if you just try and swap parts of the DOM, and then maybe swap other parts. You could think something persisted, but once you remove an input element from the DOM, focus goes away — like little funny things, like the animation states. Diving is a good way to solve that, but there’s something else that’s needed, he added. For instance, reloading the whole page may be too heavy.
Almost every framework now on the client side has a nested router.
“Maybe there’s a listing of items on the page. Each of those navigations means that when you click or change that navigation, you only have to swap out a certain section of the page,” he said. “If you change the side navigation, then everything on the right side swaps; if you change the tab bar, everything below the tabs swap. So that’s the idea behind nested routing.”
Developers can use that idea to do partials as a mechanism to basically do a hybrid routing solution, he suggested.
“If we make our islands the size of say a whole route, all we’re doing then is passing the props into some giant island. So we basically have kind of a JSON API, because you switch the route; and then suddenly, you’re just serializing the props to that island and you’re not really rendering any HTML,” he said.
When developers persist the state in islands and add client-side routing, that’s basically what React server components are, he said. There are variations on this theme; for instance, Astro recently added support for persistence in their view transition API, he said.
Astro offers some client-side routing and Qwik has been working on something similar called containers, which they’re designing from micro frontends, he said. He also said Solid has worked up some demos on the approach.