<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eric L. Goldstein</title>
    <description>The latest articles on DEV Community by Eric L. Goldstein (@mangs).</description>
    <link>https://dev.to/mangs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1065249%2F08e19084-6dcd-4a3e-aaf4-ddf759f504ca.jpeg</url>
      <title>DEV Community: Eric L. Goldstein</title>
      <link>https://dev.to/mangs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mangs"/>
    <language>en</language>
    <item>
      <title>Exploring Web Rendering: Server Components Architecture</title>
      <dc:creator>Eric L. Goldstein</dc:creator>
      <pubDate>Wed, 22 Nov 2023 21:03:14 +0000</pubDate>
      <link>https://dev.to/mangs/exploring-web-rendering-server-components-architecture-5681</link>
      <guid>https://dev.to/mangs/exploring-web-rendering-server-components-architecture-5681</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://www.flickr.com/photos/184594136@N08/52686707951"&gt;Alabama Extension&lt;/a&gt; on Flickr&lt;/p&gt;

&lt;p&gt;Holes. You’re about to read about holes. Like in the ground. What? Server components architecture can be a confusing topic, so we’re going to build on all the knowledge gained in this article series so far until this fundamental shift in application architecture makes sense. We’ve made it to the final entry in this series, so thank you for following along to the end!&lt;/p&gt;

&lt;p&gt;Let’s recap what we’ve covered so far. The case for server-first rendering was made since the first article, but each blog post hopefully made an increasingly convincing case for why doing so is essential for good performance. Starting at the beginning with &lt;a href="https://dev.to/mangs/exploring-web-rendering-isomorphic-javascript-hydration-k65"&gt;part 1 of the series&lt;/a&gt;, isomorphic JavaScript includes code that runs unchanged on both server and client. However, all components on the page require their code to be sent to the browser for hydration purposes. Thus, a non-trivial startup cost exists when initializing the application code to make the server-rendered HTML interactive and is manifested by stutters and missing interactivity until the JavaScript executes to completion; this phenomenon is known as the &lt;a href="https://web.dev/articles/rendering-on-the-web#a_rehydration_problem_one_app_for_the_price_of_two"&gt;“uncanny valley”&lt;/a&gt;. As outlined in &lt;a href="https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33"&gt;article 2&lt;/a&gt;, one way to reduce the startup penalty is to use partial hydration a.k.a. “islands” wherein the amount of code sent to the browser is reduced by only sending interactive component code. Another performance-enhancing option is to delay when hydration occurs; this is known as progressive hydration and was covered in &lt;a href="https://dev.to/mangs/exploring-web-rendering-progressive-hydration-563m"&gt;part 3&lt;/a&gt;. Near-elimination of JavaScript can be accomplished with streaming HTML – covered in &lt;a href="https://dev.to/mangs/exploring-web-rendering-streaming-html-ba9"&gt;part 4 of this series&lt;/a&gt; – by server rendering all static components as-is and sending placeholder content for those in the process of performing an asynchronous action like data fetching. As each async response completes, a new HTML “chunk” is sent back to the browser to update the page contents with almost no JavaScript; this repeats until the page is fully built.&lt;/p&gt;

&lt;p&gt;Combining the techniques from those 4 articles and adding some new ideas can lead to a unique application architecture known as server components; how this combination works will be covered after the upcoming explanation of server components mechanics. To be clear, “server components” and “server components architecture” are not the same thing. The former refers to a type of component used in a component tree, specifically the server-only variant. The latter refers to the overall structure and behavior of applications using new frameworks such as &lt;a href="https://nextjs.org/docs/app/building-your-application/rendering/server-components"&gt;React Server Components&lt;/a&gt;. Despite the frontend ecosystem’s overwhelming focus on React Server Components, server components architecture is not just part of a new version of React. In fact, it is available in frameworks like &lt;a href="https://nuxt.com/docs/guide/directory-structure/components#server-components"&gt;Nuxt&lt;/a&gt; for Vue.js and SolidStart for SolidJS – documentation coming soon but its &lt;a href="https://solid-movies.app/"&gt;Solid Movies demo app&lt;/a&gt; using only ~16kB of JavaScript has &lt;a href="https://github.com/solidjs/solid-start/tree/main/archived_examples/movies"&gt;code available&lt;/a&gt; using its experimental islands router as a stand-in for upcoming server components support. Rather, server components should be thought of as an architecture that further shifts the developer mindset from the traditional SPA client-first one started roughly a decade ago to a server-first one with unique abilities. For simplicity and relevance, React Server Components using &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; 14 will be used for examples throughout the rest of this article.&lt;/p&gt;

&lt;p&gt;The server components architecture consists of two component types: (1) its namesake server components and (2) client components. Before this new architecture, single-page applications relied on components that were rendered in the browser by default. Using server components reverses this trend because components are now rendered on the server by default. Additionally, server components are truly server-only meaning that they never render on the client. As a result, numerous benefits are gained related to data fetching, performance, security, and user experience. Data fetching and performance are improved because the rendering server is likely closer to its data source than most end user devices, so making multiple round trips to a database, for example, will occur more quickly between data centers than between a data center and a user’s browser. Also, the code needed to fetch that data remains on the server including any API keys and secret information, so the data architecture remains simpler and bundle sizes smaller. User experience is impacted greatly by performance, so improvements here clearly cause knock-on effects for UX due to less server response delay and less code in the browser as explained above.&lt;/p&gt;

&lt;p&gt;Client components, on the other hand, are a bit of a misnomer: they are isomorphic and execute identically on both the server and client with the additional benefit that server components can send props directly to specific client components; this ability to do hybrid rendering with server-client prop exchange at arbitrary points in time is a unique advantage server components architecture has over islands architecture. Additionally, client components are effectively persistent islands so when navigating pages, the “static” parts of the page (outside the islands) are sent as an API response from the server then diff’d with the current client virtual DOM to output the final real DOM; see the figure below for a visualization. Note that React now has the ability to update page content by diffing not only locally-rendered DOM nodes but ones rendered and sent by the server in a &lt;a href="https://dev.to/builderio/a-visual-explanation-of-react-server-components-1e0a#for-the-nerds-i-find-the-streaming-format-very-interesting"&gt;special, JSON-like VDOM format&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmglnj9c92lpkatiawj97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmglnj9c92lpkatiawj97.png" alt="Diagram showing how client components can persist across server-generated page changes" width="800" height="874"&gt;&lt;/a&gt;Client components persist across page navigations despite the new page’s HTML coming from the server in the form of VDOM “JSON”&lt;/p&gt;

&lt;p&gt;Now that the definition is better understood, why the focus on “holes” at the start of the article? In the component tree, the two types of components are used in mutually exclusive layers, so there is effectively a boundary between client and server components. A component with a &lt;a href="https://react.dev/reference/react/use-client"&gt;“use client”&lt;/a&gt; directive as its first line of code means that that component is a client component, but that component is only a boundary if it causes the component type to change; in other words, it is the first child client component of a server component, so a “hole” is created in the component graph to signify that the server must wait for the browser to fill in that part of the component tree. A client component that is a child of another client component does not create a boundary because one was already created by one of its parents. Conversely, a server component that is a child of a client component means a server component boundary was crossed so a “hole” is created in the component graph here as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgb01t05mj9yrq0dk0xq3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgb01t05mj9yrq0dk0xq3.png" alt="Diagram showing an example of component boundaries" width="800" height="908"&gt;&lt;/a&gt;Server components architecture requires boundaries to be crossed when switching component types. Behold the donut “holes”.&lt;/p&gt;

&lt;p&gt;An important restriction is that server components cannot be imported into client components. However, server components can be children of client components. Thus, to get around the restrictions, pass server components to client components as props; most commonly a server component is passed as the children prop.&lt;/p&gt;

&lt;p&gt;Let’s build a simple example to more clearly show how this architecture works including nesting a server component inside a client component. To make this easier to follow, an open source &lt;a href="https://github.com/mangs/server-components-simple-demo"&gt;repository was created&lt;/a&gt; to house this demo application for easier tinkering and learning. Imagine a basic blog setup that allows sharing the first paragraph of content on social media. This could all be done in a server component except for the needed feedback from the user about if and when to share. Thus, to use server components as efficiently as possible, we will use the architecture diagrammed below showing a root &lt;code&gt;&amp;lt;BlogPost&amp;gt;&lt;/code&gt; server component wrapping a &lt;code&gt;&amp;lt;Share&amp;gt;&lt;/code&gt; client component which has server rendered content directly passed to its children prop; notice the similarities to the diagram above as well especially when considering boundaries being crossed and how the client component contents are mostly rendered by a server component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwovrpjjsbf5vyg5mj4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwovrpjjsbf5vyg5mj4d.png" alt="Diagram showing the component boundaries of the demo application and how even the client component relies on server-rendered content" width="800" height="745"&gt;&lt;/a&gt;Notice the 2 boundaries crossed (“holes”) and how the &amp;lt;Share&amp;gt; component is mostly rendered by the server&lt;/p&gt;

&lt;p&gt;Examining the source code of both components can further illuminate the behavior and mechanics of React Server Components with Next.js.&lt;/p&gt;

&lt;p&gt;First the &lt;a href="https://github.com/mangs/server-components-simple-demo/blob/main/components/BlogPost/BlogPost.tsx"&gt;&lt;code&gt;&amp;lt;BlogPost&amp;gt;&lt;/code&gt; server component&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// External Imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Internal Imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getParagraphs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../utils/content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Share&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../Share/Share&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Component Definition&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paragraphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getParagraphs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paragraphs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;paragraphText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Share&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paragraphText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paragraphText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Share&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paragraphText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paragraphText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because BlogPost does not have a “use client” directive on its first line, it is a server component, so all rendering and data fetching occurs on the server. Additionally, React Server Components encourages the use of asynchronous components to promote colocation of rendering and data fetching, so the BlogPost component uses async/await to load paragraph data before rendering. Such a design typically introduces waterfalls into the network graph because the server has to pause during each network fetch. However, Next.js argues that &lt;a href="https://vercel.com/blog/understanding-react-server-components#rscs-interleaving-and-suspense-integration"&gt;waterfalls are mostly prevented&lt;/a&gt; because global &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-server-with-fetch"&gt;fetch() is replaced with a Next-specific version by default so that all requests can be memoized&lt;/a&gt;. The getParagraphs() function is meant to mimic a call to a CMS or something similar, so once its data is returned, rendering can begin. The first paragraph is rendered to the &lt;code&gt;&amp;lt;Share&amp;gt;&lt;/code&gt; component and all other paragraphs are rendered into basic &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;Then the &lt;a href="https://github.com/mangs/server-components-simple-demo/blob/main/components/Share/Share.tsx"&gt;&lt;code&gt;&amp;lt;Share&amp;gt;&lt;/code&gt; client component&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// External Imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Local Functions&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shareContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Sharing content: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Component Definition&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Share&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsPending&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setIsPending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;shareContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setIsPending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isPending&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting with a “use client” directive signifies that &lt;code&gt;&amp;lt;Share&amp;gt;&lt;/code&gt; is a client component. To help illustrate the lessons of this article as clearly as possible, this component is a simple approximation of what sharing content on social media could eventually look like without the implementation complexity. Specifically, a paragraph is shown with a green background when no sharing is occurring and a yellow background while sharing is in progress. Note the contents of the paragraph rendered by the component’s “children” prop coming from the &lt;code&gt;&amp;lt;BlogPost&amp;gt;&lt;/code&gt; server component. In other words, &lt;code&gt;&amp;lt;Share&amp;gt;&lt;/code&gt; is a client component whose contents are mostly rendered by the server. Within the onClick() function, the three lines of code change the background color to yellow, then use setTimeout() to wait for the next tick of the event loop to execute shareContent(), and finally wait 100ms to set the background color back to green. The reason for the two setTimeout() calls is to ensure background color changes occur independently from showing the alert() box.&lt;/p&gt;

&lt;p&gt;Revisiting an earlier-mentioned topic, how can this series’ 4 previous article subjects be combined into the server components architecture? The architecture does introduce new innovations unique to what came before it such as the server components component type, but it does rely on previous ideas as a basis for its functionality. The &lt;a href="https://dev.to/mangs/exploring-web-rendering-isomorphic-javascript-hydration-k65"&gt;first article in the series&lt;/a&gt; covered isomorphic rendering and hydration which is a requirement for client components to function. However, because server components execute entirely on the server with no client hydration, they have more in common with old school PHP server-side applications than they do with modern frameworks. Client components are more than just isomorphic components, otherwise application state would be lost during page navigation because new server rendered HTML would overwrite client component HTML. Instead, each client component root can be thought of as a persistent island wherein island state is preserved across page loads; &lt;a href="https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33"&gt;part 2 of this article series&lt;/a&gt; covers partial hydration which is a prerequisite for such functionality. Progressive hydration covered in part 3 is an optional performance tweak during page load to delay hydration until a component scrolls into view or the browser is idle, for example; hydrating this way is optional with the server components architecture. Streaming HTML mentioned in part 4 of this blog series is also optional but is made more powerful with components rendered only on the server. Older application architectures had two options for server rendered components: (1) a server-only application with no hydration or interactivity leading to a poor user experience, or (2) an isomorphic application with controlled interactivity and better user experience but hydration potentially reducing initial interaction performance. Streaming HTML with the server components architecture allows further refinement by choosing whether or not hydration is combined with streaming at a component rather than application level by streaming with &lt;a href="https://react.dev/reference/react/Suspense"&gt;&lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;&lt;/a&gt; within server or client components; thus, more fine-grained control over behavior, performance, and bundle size is allowed. Overall, while not all strictly required, the 4 previous article techniques – isomorphic rendering, hydration, partial &amp;amp; progressive hydration, and streaming HTML – are foundational to understanding server components architecture.&lt;/p&gt;

&lt;p&gt;Somehow this 5-part, performance-focussed learning adventure has reached an end, and I could not be more thankful for you hanging in there to the end! I certainly hope you learned something new. If you have any questions or feedback about this topic or any others, please share your thoughts with &lt;a href="https://twitter.com/EricLGoldstein"&gt;me on Twitter&lt;/a&gt;. I would certainly enjoy hearing from you! And be sure to check out &lt;a href="https://twitter.com/BabbelBytes"&gt;Babbel’s engineering team on Twitter&lt;/a&gt; to learn more about what’s going on across the department. Keep exploring!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>react</category>
      <category>servercomponents</category>
    </item>
    <item>
      <title>Exploring Web Rendering: Streaming HTML</title>
      <dc:creator>Eric L. Goldstein</dc:creator>
      <pubDate>Tue, 18 Jul 2023 17:37:35 +0000</pubDate>
      <link>https://dev.to/mangs/exploring-web-rendering-streaming-html-ba9</link>
      <guid>https://dev.to/mangs/exploring-web-rendering-streaming-html-ba9</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://www.flickr.com/photos/67635079@N00/10602463485"&gt;Kylir Horton&lt;/a&gt; on Flickr&lt;/p&gt;

&lt;p&gt;Welcome back to the 4th article in this performance-focussed series about web rendering; we’ve come a long way in a short time, so thank you to those following along. This discussion began with client-side rendering (CSR) and has progressed to and encouraged server-side rendering (SSR) for performance reasons. Remember that the server is the key to this strategy because using it to render eliminates the need to use JavaScript in the browser to do so. As discussed in &lt;a href="https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33"&gt;part 2 of this series about islands rendering&lt;/a&gt;, the rapidly-growing performance divide between midrange and premium phones over the past 10 years signifies that faster hardware can no longer be relied upon for better performance. Rather, execution efficiency must be improved, and the most expensive asset download per byte is JavaScript. Thus, minimizing JavaScript’s use is key to good performance as the previous articles in this series have explained.&lt;/p&gt;

&lt;p&gt;In an attempt to prevent any terminology-related misunderstandings, “streaming” is a common term most typically associated with audio and video. However, streaming HTML is an unrelated, text-based technology focussed on delivering content to the user as quickly as possible. While both can be accomplished in a browser, the latter is the focus of this article.&lt;/p&gt;

&lt;p&gt;Streaming HTML is a very different concept from the hydration-based topics explored in this series so far. Let’s begin our investigation by providing a short history, a general definition, and an abstracted example before diving into the details. Back in 1994, &lt;a href="https://en.wikipedia.org/wiki/Netscape_Navigator"&gt;Netscape Navigator&lt;/a&gt; 1.0 beta was introduced with a feature called &lt;a href="https://blog.codinghorror.com/the-lost-art-of-progressive-html-rendering/"&gt;progressive HTML rendering&lt;/a&gt; that allowed response HTML to be periodically flushed to the browser in “chunks” until the response was complete. In fact, HTTP 1.1 later added official support for doing so named &lt;a href="https://en.wikipedia.org/wiki/Chunked_transfer_encoding"&gt;chunked transfer encoding&lt;/a&gt; wherein a stream can transfer multiple chunks hence the name “streaming”. Effectively a modern rename of the same concept, streaming rendering uses a single HTTP response to incrementally server render a page’s HTML while minimizing the amount of JavaScript used. More specifically, the initial streaming response contains static content for data-independent components and placeholder content when waiting for data to be returned. In the meantime, the HTTP connection is held open while the server waits for its data requests to complete. Each bit of response is subsequently streamed to the user’s browser as soon as it’s ready, then its respective placeholder HTML is replaced, and finally the connection closes when all data requests are complete. Conversely, traditional server rendering waits for a page’s full output to complete including all data requests before any response begins; most server-rendered web pages load this way.&lt;/p&gt;

&lt;p&gt;To provide some additional clarity, consider a dinner analogy with a customer, dinner table, waiter, kitchen, and plates of food. The customer orders 3 plates of food A (red), B (green), and C (blue) from least to most time consuming to prepare, respectively. With traditional server rendering, the waiter will wait for the kitchen to complete all 3 plates before returning them all to the table at once. Specifically, using the image below, step 1 shows plate A as complete but must wait in the kitchen. Step 2 shows plate B gets completed next followed by step 3 which shows plate C being finished last. Not till step 4 do all the plates leave the kitchen at once to be brought to the dinner table. Notice that no plates can leave the kitchen individually; the inverse is the main enhancement that streaming rendering enables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w0pXSns2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3153irb3ytgxuliuvev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w0pXSns2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3153irb3ytgxuliuvev.png" alt="Diagram showing how traditional server rendering requires all plates from the kitchen to be complete before being delivered to the customer's table" width="800" height="681"&gt;&lt;/a&gt;Traditional server rendering requires all plates to be complete before being served&lt;/p&gt;

&lt;p&gt;Conversely, streaming allows the waiter to bring each plate to the table as the kitchen completes it. Specifically, step 1 shows plate A as ready to be served. By step 2, the customer receives plate A at their table at the same time the kitchen completes plate B. Similarly, for step 3, the waiter brings plate B to the dinner table as plate C is completed. By step 4, the waiter has brought all 3 plates to the customer’s table. Clearly the customer can start eating more quickly in the streaming example; I know which waiter I would rather have!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ee0c1A9r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5outoj1tav35kcsajmcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ee0c1A9r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5outoj1tav35kcsajmcw.png" alt="Diagram showing how streaming rendering allows any plate to be delivered to the customer's table as soon as the kitchen completes it" width="800" height="670"&gt;&lt;/a&gt;Streaming rendering allows each plate to be brought to the table as it is ready&lt;/p&gt;

&lt;p&gt;Streaming responses can occur in one of two ways: (1) in-order and (2) out-of-order. As the name suggests, in-order streaming delivers HTML to the browser in the same order as authored; this means that only HTTP is required but no JavaScript! For example, if 3 data requests are needed to build the output for a page and the first request is the slowest, that first request must complete and have its HTML returned before each remaining data request has its content delivered to the page; this is a similar problem to the traditional server rendering plates example above, specifically forcing completed data requests to wait before being served. Out-of-order streaming may unfortunately require a tiny bit of JavaScript in the browser to render each chunk of HTML, but immediate response after each data request completion is a huge advantage; thus most modern frameworks including React use this method. More specifically, each placeholder is rendered with an identifier, and each content chunk is followed by a tiny bit of JavaScript used to find the respective placeholder ID then replace the HTML with the new content; the following code example will show this in detail.&lt;/p&gt;

&lt;p&gt;Now that the fundamentals are understood, a real-world approximation of &lt;a href="https://react.dev/"&gt;React&lt;/a&gt; behavior follows in order to better illuminate the details. Using the &lt;a href="https://react.dev/reference/react/Suspense"&gt;&lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;&lt;/a&gt; built-in component in combination with rendering via &lt;a href="https://react.dev/reference/react-dom/server/renderToPipeableStream"&gt;renderToPipeableStream&lt;/a&gt; (Node.js) or &lt;a href="https://react.dev/reference/react-dom/server/renderToReadableStream"&gt;renderToReadableStream&lt;/a&gt; (other runtimes) allows full out-of-order streaming support. Loading user-specific data from a database can often be a source of latency, so assume a demo homepage application with static content and a dynamic user profile dropdown if a user is logged-in. Specifically, &lt;code&gt;&amp;lt;HomepageLayout /&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Carousel /&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Offers /&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;LearnMore /&amp;gt;&lt;/code&gt; components all output static HTML whereas our &lt;code&gt;&amp;lt;UserProfile /&amp;gt;&lt;/code&gt; component returns dynamically-built HTML based upon data fetched from a database. The application could be structured like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingSpinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Carousel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Offers&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LearnMore&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React will render this top-down, so first it will render the &lt;code&gt;&amp;lt;HomepageLayout&amp;gt;&lt;/code&gt; &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts#layout-pattern"&gt;layout component&lt;/a&gt; and its children – all of which are the remaining components to render. First of these children is the Suspense boundary around &lt;code&gt;&amp;lt;UserProfile /&amp;gt;&lt;/code&gt;. Fallback component &lt;code&gt;&amp;lt;LoadingSpinner /&amp;gt;&lt;/code&gt; is shown because &lt;code&gt;&amp;lt;UserProfile /&amp;gt;&lt;/code&gt; data is being fetched on the server. Next, the &lt;code&gt;&amp;lt;Carousel /&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Offers /&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;LearnMore /&amp;gt;&lt;/code&gt; components render their static content in order. Finally, because all components have rendered once, the component tree in the browser looks something like this (changes from one code block to the next are surrounded by “START CHANGE” and “END CHANGE” HTML comments):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;START&lt;/span&gt; &lt;span class="na"&gt;CHANGE&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;suspense-placeholder-1&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingSpinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;END&lt;/span&gt; &lt;span class="na"&gt;CHANGE&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Carousel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Offers&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LearnMore&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the data is fully fetched on the server, the remaining HTML for the &lt;code&gt;&amp;lt;UserProfile /&amp;gt;&lt;/code&gt; component streams to the browser at the end of the DOM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;suspense-placeholder-1&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingSpinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Carousel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Offers&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LearnMore&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;START&lt;/span&gt; &lt;span class="na"&gt;CHANGE&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;begin&lt;/span&gt; &lt;span class="na"&gt;UserProfile&lt;/span&gt; &lt;span class="na"&gt;HTML&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;menu item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;menu item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;javascript&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;suspense-render-1&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  const suspenseRender = document
    .getElementById(‘suspense-render-1’) // Assume this works
    .previousElementSibling;
  document
    .getElementById(‘suspense-placeholder-1’)
    .replaceWith(suspenseRender);

  // Hydration code here...
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="na"&gt;UserProfile&lt;/span&gt; &lt;span class="na"&gt;HTML&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;END&lt;/span&gt; &lt;span class="na"&gt;CHANGE&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the synchronous suspense-render-1 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; gets executed, the &lt;code&gt;&amp;lt;UserProfile /&amp;gt;&lt;/code&gt; HTML gets copied into its correct placement then is hydrated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;START&lt;/span&gt; &lt;span class="na"&gt;CHANGE&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;menu item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;menu item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;END&lt;/span&gt; &lt;span class="na"&gt;CHANGE&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Carousel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Offers&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LearnMore&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HomepageLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;begin&lt;/span&gt; &lt;span class="na"&gt;UserProfile&lt;/span&gt; &lt;span class="na"&gt;HTML&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;menu item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;menu item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;javascript&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="na"&gt;suspense-render-1&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  const suspenseRender = document
    .getElementById(‘suspense-render-1’)
    .previousElementSibling;
  document
    .getElementById(‘suspense-placeholder-1’)
    .replaceWith(suspenseRender);

  // Hydration code here...
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="na"&gt;UserProfile&lt;/span&gt; &lt;span class="na"&gt;HTML&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final HTML in its desired location remains with full interactivity after hydration. The &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag can be effectively ignored because it is done executing.&lt;/p&gt;

&lt;p&gt;After reviewing its fundamental and practical aspects, streaming rendering can still be better understood by evaluating its pros and cons.&lt;/p&gt;

&lt;p&gt;Streaming rendering provides numerous benefits over traditional server rendering. Moving data fetching and rendering to the server typically involves a latency penalty because both must fully complete before a response can be sent. Streaming eliminates this restriction by allowing fallback UI elements to be rendered in place of data-dependent components until the latter can be delivered to the browser. Furthermore, as in the React example above, HTML chunks can be delivered in any order with out-of-order streaming and a tiny trailing JavaScript payload to swap the HTML into its final location. In addition to the numerous performance benefits outlined by this series, client-rendered applications enjoy security benefits by doing data fetching and rendering on the server because vendors, technologies, and API tokens are hidden away from prying eyes. And streaming applications can often feel as fast as client-rendered ones if delivered using &lt;a href="https://blog.cloudflare.com/benchmarking-edge-network-performance/"&gt;edge rendering&lt;/a&gt; – using the servers around the world and closest to your users to deliver dynamic content similar to a &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network"&gt;Content Delivery Network (CDN)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Streaming has a few downsides. Once a response begins and a response code is chosen (e.g. 200), there is no way to change it, so an error occurring during a data fetch will have to let the user know another way. Also, not all frameworks and runtimes support streaming, so choose carefully. &lt;a href="https://markojs.com/docs/troubleshooting-streaming/"&gt;Marko&lt;/a&gt; was &lt;a href="https://tech.ebayinc.com/engineering/async-fragments-rediscovering-progressive-html-rendering-with-marko/"&gt;one of the first&lt;/a&gt; (2014!) JavaScript frameworks to support streaming, and &lt;a href="https://react.dev/reference/react-dom/server/renderToReadableStream"&gt;React&lt;/a&gt; and &lt;a href="https://www.solidjs.com/docs/latest/api#rendertostream"&gt;SolidJS&lt;/a&gt; both have great support for streaming as well, for example.&lt;/p&gt;

&lt;p&gt;Our streaming journey has come to a close, so I hope it was enjoyable and enlightening! If you have any questions or feedback about this topic or any others, please share your thoughts with &lt;a href="https://twitter.com/EricLGoldstein"&gt;me on Twitter&lt;/a&gt;. I would certainly enjoy hearing from you! And be sure to check out &lt;a href="https://twitter.com/BabbelBytes"&gt;Babbel’s engineering team on Twitter&lt;/a&gt; to learn more about what’s going on across the department. The next and final article in this series will truly be an epic endeavor when we explore server components! They’re not just for React…&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>streaming</category>
    </item>
    <item>
      <title>Exploring Web Rendering: Progressive Hydration</title>
      <dc:creator>Eric L. Goldstein</dc:creator>
      <pubDate>Mon, 03 Jul 2023 20:43:40 +0000</pubDate>
      <link>https://dev.to/mangs/exploring-web-rendering-progressive-hydration-563m</link>
      <guid>https://dev.to/mangs/exploring-web-rendering-progressive-hydration-563m</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://www.flickr.com/photos/62337512@N00/3902764265" rel="noopener noreferrer"&gt;anthony kelly&lt;/a&gt; on Flickr&lt;/p&gt;

&lt;p&gt;As we reach the third article in this series focussed on exploring web rendering and performance, welcome back! Opportunities for performance improvements are really becoming apparent, so I hope you enjoy what’s coming up. If you have any questions, feel free to reach &lt;a href="https://twitter.com/EricLGoldstein" rel="noopener noreferrer"&gt;me on Twitter&lt;/a&gt; and I’ll do my best to help. As mentioned in the &lt;a href="https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33"&gt;previous article about partial hydration a.k.a. “islands”&lt;/a&gt;, 2 main options exist to more quickly make a page interactive: (1) do less work by only hydrating interactive components and ignoring the rest, or (2) hydrate in the future; the former was covered in the previous article while the latter is the subject of this article and is known as progressive hydration.&lt;/p&gt;

&lt;p&gt;With traditional hydration during page load, an undesirable race condition known as the &lt;a href="https://web.dev/rendering-on-the-web/#a-rehydration-problem-one-app-for-the-price-of-two" rel="noopener noreferrer"&gt;“uncanny valley”&lt;/a&gt; can occur when components are visible but not yet hydrated and thus non-interactive; &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement" rel="noopener noreferrer"&gt;progressive enhancement&lt;/a&gt; – using native HTML primitives such as forms as a fallback mechanism until JavaScript loads – is a typical remedy because eliminating this loss of interactivity before hydration is very difficult otherwise. However, progressive hydration delays hydration on purpose, so care must be taken so the user doesn’t wait when attempting to interact with the page. This is truly a delicate balance between minimizing performance impact and ensuring a good user experience. If done correctly, hydration will not be noticed by the user as it happens, but he or she will benefit from it.&lt;/p&gt;

&lt;p&gt;A key reason why the progressive hydration portion of this article series comes after partial hydration (a.k.a. “islands”) is because they are often used together. Remember the goal of islands is to improve performance by limiting the number of components whose code is sent to the browser; interactive components can be thought of as islands of interactivity and only that code need be downloaded. Without the ability to break an application into individual hydration targets, the only way to progressively hydrate is to do so for an entire application which would effectively be opting into uncanny valley behavior and potentially, purposefully breaking interactivity; this is certainly a good way to frustrate your users! Instead, if some islands are hydrated immediately and others later on, the aforementioned delicate balance is restored and a better performance profile at page load will result.&lt;/p&gt;

&lt;p&gt;How long do we wait to progressively hydrate? Luckily good patterns have been established by popular frameworks like &lt;a href="https://docs.astro.build/en/reference/directives-reference/#client-directives" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; and &lt;a href="https://www.11ty.dev/docs/plugins/partial-hydration/#usage" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt;. Waiting to hydrate when a component scrolls into view or when the browser is idle are the options that will likely show the benefits most clearly; using Astro, the &lt;a href="https://docs.astro.build/en/reference/directives-reference/#clientvisible" rel="noopener noreferrer"&gt;client:visible&lt;/a&gt; and &lt;a href="https://docs.astro.build/en/reference/directives-reference/#clientidle" rel="noopener noreferrer"&gt;client:idle&lt;/a&gt; directives correspond, respectively. For example, a &lt;code&gt;&amp;lt;Carousel /&amp;gt;&lt;/code&gt; React component can be hydrated when it scrolls into view by writing it as &lt;code&gt;&amp;lt;Carousel client:visible /&amp;gt;&lt;/code&gt;; that’s it! The beauty of progressive hydration is that even if those established patterns don’t meet your needs, any time or event can be chosen as a target, but you’ll likely have to write your own code to do so. To make the process easier, Astro added the ability to &lt;a href="https://docs.astro.build/en/reference/directives-reference/#custom-client-directives" rel="noopener noreferrer"&gt;add custom hydration directives&lt;/a&gt; in version 2.6, so check it out if you’re interested.&lt;/p&gt;

&lt;p&gt;To emphasize the point of how useful partial and progressive hydration can be when used together, let’s reexamine the example from the &lt;a href="https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33"&gt;previous article&lt;/a&gt; but this time with an overlay showing the boundaries of what’s rendered on screen (a.k.a. the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Viewport_concepts#what_is_a_viewport" rel="noopener noreferrer"&gt;browser viewport&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyma8ieifutqfuyqrjgbu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyma8ieifutqfuyqrjgbu.png" alt="The previous article’s island-based component structure relative to the browser viewport"&gt;&lt;/a&gt;The previous article’s island-based component structure relative to the browser viewport&lt;/p&gt;

&lt;p&gt;Three islands of interactivity exist on this page corresponding to the colored boxes: (1) the Shopping Cart component, (2) the Buy Now Button component, and (3) the Product Reviews component. From a user interest perspective, the Buy Now Button component is in the center of the screen so is likely to get the user’s attention first, and the Shopping Cart component is also on-screen and related especially if that button is clicked. However, the user can’t see the Product Reviews component at all. From an optimization perspective, progressive hydration can be used to delay execution of the Product Reviews component code until it scrolls into view; maybe the user won’t scroll down to it, so why bother? Using &lt;a href="https://docs.astro.build/en/reference/directives-reference/#clientvisible" rel="noopener noreferrer"&gt;Astro’s syntax&lt;/a&gt; as a guide, let’s add a directive to the component and render it as &lt;code&gt;&amp;lt;ProductReviews client:visible /&amp;gt;&lt;/code&gt; to see how this decision changes the performance profile of the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F111z1yx9c0lgy4eo5wt5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F111z1yx9c0lgy4eo5wt5.png" alt="Progressive hydration prevents eager download of the Product Reviews component code"&gt;&lt;/a&gt;Progressive hydration prevents eager download of the Product Reviews component code&lt;/p&gt;

&lt;p&gt;The Product Reviews component box is now white meaning that HTML is effectively static until the user scrolls it into view. Assuming these colored components all have a similar weight, approximately 33% less code runs during hydration at page load; pretty nice improvement for typing a few extra characters!&lt;/p&gt;

&lt;p&gt;Progressive hydration provides numerous benefits over eager hydration methods. The initial page load will be less impacted by hydration bottlenecks because less JavaScript code will execute. This benefit can be compounded for non-urgent components by not even downloading the code for the component until hydration needs to occur. In other words, application bundle size will be reduced while improving &lt;a href="https://web.dev/tti/" rel="noopener noreferrer"&gt;Time to Interactive&lt;/a&gt; – the elapsed time from a page load beginning until user input can be quickly and reliably responded to. However, the delicate balance between immediate reaction to user action and minimizing code download and execution will need to be weighed to best determine the correct choice for each case.&lt;/p&gt;

&lt;p&gt;A proper evaluation would not be complete without also examining the drawbacks of progressive hydration. The largest concern is the potential loss of user interactions due to missing JavaScript code pre-hydration. This can be mitigated somewhat by capturing user events such as clicks and keystrokes and replaying them when hydration has completed, but this comes with its own complexities and race conditions that are outside the scope of this article. Without a framework like Astro or Eleventy that includes handy directives to make delaying hydration easier, progressively hydrating requires a solid understanding of web APIs like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver" rel="noopener noreferrer"&gt;IntersectionObserver&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback" rel="noopener noreferrer"&gt;requestIdleCallback&lt;/a&gt;; for example, Astro’s client:visible and client:idle directives use those APIs under the hood, respectively. Thus, progressive hydration without a user-friendly framework is probably not a good choice for inexperienced engineers or teams.&lt;/p&gt;

&lt;p&gt;Now that progressive hydration’s pros and cons are understood, what are the recommended ways to try it? At the risk of being repetitive, &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; and &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; have improved the developer experience significantly in this area, so the recommended way to learn is to start with either tool to gain experience. If more granularity is desired, build on top of either framework’s primitives or eventually build your own once the concepts are mastered.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this overview of progressive hydration. If you have any questions or feedback about this topic or any others, please share your thoughts with &lt;a href="https://twitter.com/EricLGoldstein" rel="noopener noreferrer"&gt;me on Twitter&lt;/a&gt;. I would certainly enjoy hearing from you! And be sure to check out &lt;a href="https://twitter.com/BabbelBytes" rel="noopener noreferrer"&gt;Babbel’s engineering team on Twitter&lt;/a&gt; to learn more about what’s going on across the department. Stay tuned for part 4 about streaming rendering and how full page loads can act similarly to a single-page app but without most of the JavaScript: magic!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>hydration</category>
      <category>progressive</category>
      <category>performance</category>
    </item>
    <item>
      <title>Exploring Web Rendering: Partial Hydration (a.k.a. “Islands”)</title>
      <dc:creator>Eric L. Goldstein</dc:creator>
      <pubDate>Tue, 13 Jun 2023 21:59:22 +0000</pubDate>
      <link>https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33</link>
      <guid>https://dev.to/mangs/exploring-web-rendering-partial-hydration-aka-islands-3n33</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://www.flickr.com/photos/80403443@N00/33515808"&gt;tiarescott&lt;/a&gt; on Flickr&lt;/p&gt;

&lt;p&gt;Welcome back to the second part of my series focussed on exploring interesting and useful web rendering technologies. As the series progresses, you’ll notice a core theme of performance optimization by moving from client-first to server-first rendering. In fact, the reason for the ordering of these articles is to progress towards as little browser JavaScript as possible while maintaining interactivity. Starting from a traditional client-rendered single page application, if you follow these techniques in order, you can end up with far less JavaScript sent to the browser than when you started. As a reminder, when optimizing for performance, less application code means less to download and execute in order to accomplish the user’s intent. Considering the accelerating divergence between high-end and mainstream phones over the past 10 years as shown in the graph below, hardware is providing meaningful performance improvements only on expensive devices. Thus, as time goes on, making applications with efficient resource usage is becoming essential to guarantee a good user experience for most users because common devices are not getting noticeably faster. Remember: JavaScript is primarily a &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Main_thread"&gt;single-threaded language&lt;/a&gt; by design, and threads execute on cores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OMmkeoM8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bgiankjpsce8e2g2evc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OMmkeoM8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bgiankjpsce8e2g2evc.png" alt="Chart showing the single-core performance discrepancy between expensive and mainstream devices" width="800" height="354"&gt;&lt;/a&gt;From &lt;a href="https://infrequently.org/"&gt;Alex’s Russel&lt;/a&gt;’s talk &lt;a href="https://www.youtube.com/watch?v=BmiVevOUvho"&gt;The Global Baseline, 2022&lt;/a&gt; @ performance.now() 2022&lt;/p&gt;

&lt;p&gt;As discussed in the &lt;a href="https://dev.to/mangs/exploring-web-rendering-isomorphic-javascript-hydration-k65"&gt;previous article in this series&lt;/a&gt;, hydration initializes an  application by adding interactivity to server-rendered HTML. Doing so can cause serious performance problems due to the entire application needing to render before becoming interactive; &lt;a href="https://www.speedcurve.com/blog/javascript-long-tasks/"&gt;long tasks&lt;/a&gt;, UI stutter, and even pauses can be consequences of this heavy-handed initialization strategy. There are two effective ways to tackle this problem: (1) do less work when hydrating by skipping the portions of the DOM that will never be interactive (a.k.a. “partial hydration”) or (2) do hydration at some point in the future (a.k.a. “progressive hydration”); only the former will be the focus of this article, then the latter will be covered in part 3 of this series.&lt;/p&gt;

&lt;p&gt;Before examining its details, partial hydration will be best understood by first exploring its origins to appreciate how we arrived at where we are today. The concept first arose with the &lt;a href="https://markojs.com/"&gt;Marko&lt;/a&gt; framework by eBay back in 2014 with a feature they now refer to as &lt;a href="https://markojs.com/#hydration"&gt;“Code Elimination”&lt;/a&gt; on their homepage. eBay’s motivation for creating a framework like Marko is clear once you realize the &lt;a href="https://twitter.com/nucliweb/status/1585904003530903552"&gt;direct correlation between performance and revenue&lt;/a&gt; and the lack of sufficient JavaScript ecosystem options at the time to meet their performance needs. In fact, their timing is interesting because Marko is &lt;a href="https://dev.to/ryansolid/what-has-the-marko-team-been-doing-all-these-years-1cf6"&gt;one of the first examples of a full-stack, server-first JavaScript framework&lt;/a&gt;, a clear indication of how important this design philosophy is to performance optimization because of the scale at which the framework has been operating since then. If you’re interested in more details about eBay’s motivations for creating Marko, &lt;a href="https://twitter.com/RyanCarniato"&gt;Ryan Carniato&lt;/a&gt; – creator of &lt;a href="https://www.solidjs.com/"&gt;SolidJS&lt;/a&gt; and previously a Marko core team member – wrote an article &lt;a href="https://dev.to/ryansolid/what-has-the-marko-team-been-doing-all-these-years-1cf6"&gt;What has the Marko Team Been Doing all These Years?&lt;/a&gt; that delves deeper.&lt;/p&gt;

&lt;p&gt;Where did the modern “islands” naming come from? The idea of “component islands” was named by &lt;a href="https://twitter.com/ksylor"&gt;Katie Sylor-Miller&lt;/a&gt; – frontend architect at Etsy – back in 2019 and expanded upon by Preact creator &lt;a href="https://twitter.com/_developit"&gt;Jason Miller&lt;/a&gt; in his blog post entitled &lt;a href="https://jasonformat.com/islands-architecture/"&gt;Islands Architecture&lt;/a&gt;. Within it, he explains how “islands” can be thought of like independent single page applications wherein top-down rendering is not required for the full page output to be built. Rather, each island is an isolated unit, so any potential performance problems in one “application” do not affect any others. A side effect of combining many single page apps (SPAs) onto a single page is that client side routing is lost because they all cannot control the browser’s URL, thus the application must be a multi-page app (MPA) with full-page reloads; stay tuned for part 5 of this series for how server components help solve that problem. He continues to explain that because the full HTML is returned to the browser, SEO and accessibility benefit because web crawlers and assistive technologies inherently understand the semantics of static HTML better than a dynamically changing DOM. Navigating with &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags allows assistive technologies to understand how to move between pages using the semantics of HTML instead of needing to reinvent the wheel with custom components and JavaScript in order for &lt;a href="https://www.a11yproject.com/"&gt;a11y&lt;/a&gt; technologies to understand navigation; no JavaScript is used as a result which is a performance win.&lt;/p&gt;

&lt;p&gt;Now that the history is understood, onto the main topic: what is partial hydration? Let’s compare it to full hydration to make the behavior more clear. Remember, hydration usually occurs after loading a full page of HTML. Let’s use the diagram below as a visual aid wherein the largest white box represents the whole page and each white, red, green, and blue box represents a component; the non-white boxes are interactive components a.k.a. islands. Imagine a server preparing what JavaScript to send, then draw a box around each component that needs its JS sent to the browser. The full hydration case is quite easy: every component will have a box around it, so all code gets sent to the browser. According to the diagram, all 8 components’ code should be sent including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nav Bar&lt;/li&gt;
&lt;li&gt;Shopping Cart&lt;/li&gt;
&lt;li&gt;Product Image&lt;/li&gt;
&lt;li&gt;Buy Now Button&lt;/li&gt;
&lt;li&gt;Product Description&lt;/li&gt;
&lt;li&gt;Product Reviews&lt;/li&gt;
&lt;li&gt;Related Products&lt;/li&gt;
&lt;li&gt;Footer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ASo4_6Uw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3afuzrhhm1tevgyjbx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ASo4_6Uw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3afuzrhhm1tevgyjbx4.png" alt="Diagram of how islands rendering works" width="800" height="972"&gt;&lt;/a&gt;Each box is a component. Island areas filled in color, static areas outlined in white.&lt;/p&gt;

&lt;p&gt;Partial hydration should include only interactive components and their children. Specifically, draw a colored line around only the interactive components on the page; the diagram above shows exactly this, and if you squint a little, they look like islands of interactivity hence their name. Now only 3 components’ code should be sent including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Shopping Cart&lt;/li&gt;
&lt;li&gt;Buy Now Button&lt;/li&gt;
&lt;li&gt;Product Reviews&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With partial hydration, only ⅜ or 37.5% of components have their JavaScript sent to the browser! The smaller your islands, the bigger the benefits because the ultimate goal is minimizing the amount of JavaScript sent to the browser as evidenced by myriad articles &lt;a href="https://web.dev/optimize-long-tasks/"&gt;on the subject&lt;/a&gt;. Because server-side rendering (SSR) is used, the non-interactive component code is never sent to the browser because it would never serve a purpose: if that HTML remains static, why send code that will never change it? Instead, only code for the interactive islands is sent which has been shown to reduce the amount of code sent to the browser. As a result, less code is downloaded, parsed, and executed, so &lt;a href="https://web.dev/tti/"&gt;Time to Interactive&lt;/a&gt; is minimized by running less code that delays the user’s ability to interact with the page; in other words, users can click that Buy Now Button more quickly which increases the &lt;a href="https://www.contentkingapp.com/academy/page-speed-resources/faq/amazon-page-speed-study/"&gt;chances of a sale&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Partial hydration provides numerous benefits over traditional hydration. Less JavaScript required for an identical amount of interaction has no downsides aside from requiring MPA navigation; streaming rendering (part 4 of this series) and the full-page load portion of the &lt;a href="https://developer.chrome.com/docs/web-platform/view-transitions/"&gt;View Transitions API&lt;/a&gt; (animations are fun!) can help hide the additional delay from an MPA navigation, but the loss of application state on page change should remain a concern. Using islands, faster page loads will result which will make users happier and &lt;a href="https://developers.google.com/search/docs/appearance/core-web-vitals"&gt;push your pages higher in search rankings&lt;/a&gt;. Furthermore, less code is shared between server and browser, so the mental overhead required for dual-mode rendering in isomorphic rendered applications disappears for static portions of the app because those regions only run on the server; read part 1 of this series for more on how mental overhead can affect isomorphic application development. A further benefit is parallel loading of island code because each island is completely independent, so each island can hydrate as soon as it’s ready. Page load performance can be further improved by delaying hydration of some or all islands into the future; the specifics of this are detailed in the following article in this series (part 3) about progressive hydration.&lt;/p&gt;

&lt;p&gt;Unfortunately partial hydration has some drawbacks as well. The first and most obvious is requiring full-page reloads during navigation; server components are effectively persistent islands which can mitigate this and will be covered in part 5 of this article series. Traditionally, partial hydration was very difficult to set up. You effectively needed to design a build process that dynamically created multiple render roots for multiple component trees and the ability to pass props to each one independently in addition to being able to share state between them. A good example of this is &lt;a href="https://markus.oberlehner.net/"&gt;Markus Oberlehner&lt;/a&gt;’s terrific overview of this process in his blog post entitled &lt;a href="https://markus.oberlehner.net/blog/building-partially-hydrated-progressively-enhanced-static-websites-with-isomorphic-preact-and-eleventy/"&gt;Building Partially Hydrated, Progressively Enhanced Static Websites with Isomorphic Preact and Eleventy&lt;/a&gt;. The process he describes is very manual and was written before the advent of modern islands solutions included in frameworks like &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt;. Additionally, state sharing between islands is made more complex than a traditional context-based design because there is no shared component root. Luckily, however, &lt;a href="https://www.solidjs.com/docs/latest/api#createsignal"&gt;signals&lt;/a&gt; and &lt;a href="https://github.com/nanostores/nanostores#nano-stores"&gt;signal-like&lt;/a&gt; libraries have been developed that can tackle this problem very efficiently and effectively; some libraries such as the &lt;a href="https://docs.astro.build/en/core-concepts/sharing-state/"&gt;Astro-recommended Nano Stores&lt;/a&gt; are less than 1 kB! Thus, whether this is truly a problem is a matter of perspective.&lt;/p&gt;

&lt;p&gt;After all this promise of performance improvement, how can partial hydration be used today? The easiest and most flexible option is &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt; because you can not only choose from a variety of frameworks for your islands like &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;, &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt;, &lt;a href="https://vuejs.org/"&gt;Vue&lt;/a&gt;, and &lt;a href="https://www.solidjs.com/"&gt;SolidJS&lt;/a&gt;, but you can even mix-and-match &lt;a href="https://docs.astro.build/en/guides/integrations-guide/#official-integrations"&gt;different frameworks&lt;/a&gt; in islands on the same page! It even supports deployment adapters for serverless and edge rendering platforms too with only &lt;a href="https://github.com/mangs/homepage/commit/abac2fdab70fa1bbe797632c7aa90af379d6e560#diff-e0f0c5adbe0b9ca5d0b57caf5cea33a8d88899fd02a43df1e9862b185f8a1e5fR5-R8"&gt;a few line changes necessary&lt;/a&gt; in your config file. If you’re a fan of &lt;a href="https://deno.com/runtime"&gt;Deno&lt;/a&gt;, the &lt;a href="https://fresh.deno.dev/"&gt;Fresh&lt;/a&gt; framework has great support for islands as well. The most-performant but least well-known option is &lt;a href="https://markojs.com/"&gt;Marko&lt;/a&gt;, especially the upcoming &lt;a href="https://dev.to/ryansolid/fluurt-re-inventing-marko-3o1o"&gt;Marko 6&lt;/a&gt; release whose compiler optimizations and support for &lt;a href="https://dev.to/this-is-learning/resumability-wtf-2gcm"&gt;resumability&lt;/a&gt; and &lt;a href="https://dev.to/ryansolid/a-hands-on-introduction-to-fine-grained-reactivity-3ndf"&gt;fine-grained reactivity&lt;/a&gt; will be truly something special.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this overview of the islands architecture. If you have any questions or feedback about this topic or any others, please share your thoughts with &lt;a href="https://twitter.com/EricLGoldstein"&gt;me on Twitter&lt;/a&gt;. I would certainly enjoy hearing from you! And be sure to check out &lt;a href="https://twitter.com/BabbelBytes"&gt;Babbel’s engineering team on Twitter&lt;/a&gt; to learn more about what’s going on across the department. Stay hydrated!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>hydration</category>
      <category>islands</category>
      <category>performance</category>
    </item>
    <item>
      <title>Exploring Web Rendering: Isomorphic JavaScript &amp; Hydration</title>
      <dc:creator>Eric L. Goldstein</dc:creator>
      <pubDate>Tue, 30 May 2023 18:21:41 +0000</pubDate>
      <link>https://dev.to/mangs/exploring-web-rendering-isomorphic-javascript-hydration-k65</link>
      <guid>https://dev.to/mangs/exploring-web-rendering-isomorphic-javascript-hydration-k65</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://www.flickr.com/photos/30713600@N00/3229094550" rel="noopener noreferrer"&gt;Martin Abegglen&lt;/a&gt; on Flickr&lt;/p&gt;

&lt;p&gt;What an exciting time the past few years have been in the evolution of web rendering technologies! Our team at Babbel is hyper-focussed on performance improvements to our applications this year, and we’re going to be using some very cool new concepts and technologies to achieve our goals. As a Principal Engineer, rather than keep my research and knowledge sharing within our walls, I’ve decided to share what I’m thinking about in hopes to expose and teach these topics to a wider audience, grow a dialog with the wider community, and to reinforce my own knowledge - the best way to learn is to teach after all. Before starting, thank you to &lt;a href="https://twitter.com/RyanCarniato" rel="noopener noreferrer"&gt;Ryan Carniato&lt;/a&gt; - creator of SolidJS - whose &lt;a href="https://www.youtube.com/@ryansolid" rel="noopener noreferrer"&gt;YouTube streams&lt;/a&gt; and &lt;a href="https://dev.to/ryansolid/"&gt;dev.to articles&lt;/a&gt; have been a huge inspiration and source of guidance for my learning and writing about many technologies and frameworks; I strongly encourage you to follow his work. Without further ado, please join me in my 5-part series exploring some of the more interesting modern web rendering techniques; this article is just the start! If you have any questions or feedback of any kind, please reach out to me on &lt;a href="https://twitter.com/EricLGoldstein" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;; I’d love to hear from you!&lt;/p&gt;

&lt;p&gt;The influx of new performance-focussed frameworks like &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;, &lt;a href="https://www.solidjs.com/" rel="noopener noreferrer"&gt;SolidJS&lt;/a&gt;, and &lt;a href="https://qwik.builder.io/" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt; has indicated we’re entering a new web era, exemplified by them topping the developer satisfaction charts for &lt;a href="https://2022.stateofjs.com/en-US/libraries/rendering-frameworks/" rel="noopener noreferrer"&gt;rendering&lt;/a&gt; and &lt;a href="https://2022.stateofjs.com/en-US/libraries/front-end-frameworks/" rel="noopener noreferrer"&gt;front-end&lt;/a&gt; frameworks respectively. In addition, considering the growing correlation between performance, search engine rank, and &lt;a href="https://www.gigaspaces.com/blog/amazon-found-every-100ms-of-latency-cost-them-1-in-sales/" rel="noopener noreferrer"&gt;revenue&lt;/a&gt;, understanding the progression of web rendering technologies has never been more important.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i8n0aujjzvr4xfaecnn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i8n0aujjzvr4xfaecnn.png" alt="Rendering framework retention rankings from State of JS 2022"&gt;&lt;/a&gt;Rendering framework retention rankings from State of JS 2022&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fly7m77g77he0vcbmno78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fly7m77g77he0vcbmno78.png" alt="Front-end framework retention rankings from State of JS 2022"&gt;&lt;/a&gt;Front-end framework retention rankings from State of JS 2022&lt;/p&gt;

&lt;p&gt;The rise in popularity of component-based, single page app frameworks in the early to mid 2010s improved developer experience with the introduction of declarative UIs and simpler APIs. Such simplicity seeking led to rendering being done in the browser, otherwise known as client-side rendering (CSR). While reasoning about an app during development was now simpler than before, application initialization performance usually suffered because now the entire app lived in JavaScript which required it to be downloaded, parsed, then executed thus delaying HTML parsing and asset downloads; beforehand the user won’t see anything at all! An alternative was eventually evangelized known as isomorphic (a.k.a. universal) JavaScript which attempted to combine the best of what the server and browser had to offer.&lt;/p&gt;

&lt;p&gt;Ultimately, isomorphic rendering of JavaScript means that a single application can be used on both a server and browser. Specifically, the server renders the application using a technique called server-side rendering (SSR) and the client uses the application to hydrate the HTML page in order to make it interactive; we’ll cover hydration a bit later. Traditionally, server-side rendering involves server-based routing meaning a “full page load”. In other words, when you click a link to navigate to a new page, the server decides what happens when the click occurs including what content is displayed on the next page. Contrast that with client-side routing where those decisions are made in the browser meaning the browser updates the URL, fetches the new URL’s data via API if needed for the route, then updates the UI; consider this a partial page load because usually only the part of the page that needs to change is updated but the rest is unaffected (e.g. the app shell). Isomorphic rendering is effectively a combination of the two: the server renders the full page HTML, then the client mounts the app and takes over all routing duties. In other words, the application starts off as SSR then automatically transforms and remains as CSR throughout the life of the page. See the following images for a visual comparison of the three aforementioned techniques in the same scenario: navigating from a start page (1) to two subsequent pages (2 and 3); note the differences between client-side and isomorphic routing for step 1 after which they operate identically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9uqxh0pqg0xc25zaudmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9uqxh0pqg0xc25zaudmu.png" alt="Diagram explaining server-side routing; note all interactions cause requests to the server"&gt;&lt;/a&gt;Diagram explaining server-side routing; note all interactions cause requests to the server&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18bnwfswudoa4zs8tp8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18bnwfswudoa4zs8tp8b.png" alt="Diagram explaining client-side routing; note the client does all the work including on initial render (1)"&gt;&lt;/a&gt;Diagram explaining client-side routing; note the client does all the work including on initial render (1)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmczwt1ag3v7ztcxmd5q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmczwt1ag3v7ztcxmd5q2.png" alt="Diagram explaining isomorphic routing; note the server initial render (1) then the client does all the work"&gt;&lt;/a&gt;Diagram explaining isomorphic routing; note the server initial render (1) then the client does all the work&lt;/p&gt;

&lt;p&gt;Remember that an isomorphic application first involves a server rendering an HTML page using a JavaScript application then a browser hydrating that page using the same application the server used. So what is hydration? Let’s define it: hydration is the process of initializing an application to make a server-rendered HTML page interactive by setting up the necessary application state and event listeners. Traditionally, hydration downloads and executes all components in your application eagerly meaning that doing so occurs as soon as possible, but there are other, potentially more performant options which will be explored in future articles in this series including partial (part 2) and progressive hydration (part 3).&lt;/p&gt;

&lt;p&gt;Because the application is shared between server and browser, state is as well. If your app has a single source of state as is often the case with &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;, for example, sharing state between server and browser can be as simple as passing your state object through &lt;code&gt;JSON.stringify()&lt;/code&gt; then adding it to a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element at the end of your HTML output so as to not be render blocking; more elaborate state models require more complex methods of state sharing.&lt;/p&gt;

&lt;p&gt;On the server, the DOM is non-existent so its events can effectively be ignored, but on the client they’re treated normally. Using React as an example, when an application is rendered with &lt;a href="https://react.dev/reference/react-dom/client/hydrateRoot" rel="noopener noreferrer"&gt;React.hydrateRoot()&lt;/a&gt;, React is smart enough to know that hydration is client-only so event listeners should be bound. However, when rendering that same application with &lt;a href="https://react.dev/reference/react-dom/server/renderToString" rel="noopener noreferrer"&gt;React.renderToString()&lt;/a&gt;, binding event listeners is skipped because that function is part of React’s server-only API. As a result, occasionally your code will need to be environment-aware so client- or server-specific actions can be taken when appropriate. Clearly, while your application may be shared between server and browser, isomorphic rendering has additional mental overhead during development that must be factored in when choosing your desired rendering model.&lt;/p&gt;

&lt;p&gt;Isomorphic applications can provide multiple benefits over client-rendered ones. Instead of an empty HTML shell like what’s often used to bootstrap a CSR app, a server renders the entire page’s content into HTML which allows browsers to begin parsing the page HTML sooner. Thus, page assets like images, JS, and CSS begin downloading earlier. Similarly, SEO is improved because web crawlers like googlebot can crawl your pages more quickly with the full page’s content available rather than having to wait for all client API requests to respond and the DOM to incrementally update; the latter can be time-consuming and error-prone for crawlers. Furthermore, because the HTML is fully available and assuming the response per page doesn’t change, you can optionally CDN cache or static build your pages for a performance boost, especially the prior which will cause the response to skip talking to your origin server and be returned from as close to the user as possible; latency and server load is minimized as a result. If your application values quick navigation times, the client-side routing of an isomorphic application after initial page load should act similarly to a CSR app with no server rendering.&lt;/p&gt;

&lt;p&gt;For a complete analysis, downsides must be explored as well. Ignoring streaming rendering for now (part 4 in this series will cover it), &lt;a href="https://web.dev/ttfb/" rel="noopener noreferrer"&gt;Time to First Byte (TTFB)&lt;/a&gt; for isomorphic rendered HTML is larger than CSR’s static app shell HTML because all server-side API requests need to complete before being included in the returned HTML. In addition, total isomorphic payload size is larger because the aforementioned completed request data is included within the HTML. However, the ability to parse and download assets sooner usually outweighs that downside and results in an overall faster page load. Eager hydration can cause a period of unresponsiveness known as a &lt;a href="https://web.dev/long-tasks-devtools/" rel="noopener noreferrer"&gt;long task&lt;/a&gt; which can negatively affect &lt;a href="https://web.dev/tti/" rel="noopener noreferrer"&gt;time to interactive&lt;/a&gt;; this symptom is especially painful for devices with slow &lt;a href="https://browser.geekbench.com/processor-benchmarks" rel="noopener noreferrer"&gt;single-threaded&lt;/a&gt; CPUs (I love hardware, too!). In other words, if too much hydration occurs all at once, users will experience freezing of the user interface during page load which is when they are most likely to be interested in interaction; the following articles in this series discuss ways to mitigate this and other problems to improve user experience. Because isomorphic rendering relies on JavaScript for interactivity, user attempts to click a button for example will have no effect until hydration completes; this example ignores &lt;a href="https://blog.logrocket.com/understanding-progressive-enhancement/" rel="noopener noreferrer"&gt;progressive enhancement&lt;/a&gt;, and mitigation techniques do exist but they often involve loading tiny bits of JavaScript even earlier. Finally, the &lt;a href="https://dev.to/this-is-learning/why-efficient-hydration-in-javascript-frameworks-is-so-challenging-1ca3"&gt;“double data” problem&lt;/a&gt; is an Achilles heel of the design and refers to data needing to be downloaded twice: once embedded into the HTML as content and again in the serialized JSON used to hydrate the application. An example of this repetition is the body of a blog post which gets rendered directly into the HTML and encoded into the JSON used for hydration; if the post content is the vast majority of your page weight, the page has now become effectively twice as large.&lt;/p&gt;

&lt;p&gt;Now that isomorphic rendering has been explained, how can it be used? The most easy-to-use options are known as “metaframeworks” which take a base rendering framework like &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, &lt;a href="https://qwik.builder.io/" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt;, or &lt;a href="https://www.solidjs.com/" rel="noopener noreferrer"&gt;SolidJS&lt;/a&gt; and add additional features like routing and style management resulting in options like &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://qwik.builder.io/docs/qwikcity/" rel="noopener noreferrer"&gt;QwikCity&lt;/a&gt;, and &lt;a href="https://start.solidjs.com/" rel="noopener noreferrer"&gt;SolidStart&lt;/a&gt;, respectively, among others. Try them out and see what works best for you. If you have any questions or feedback about this topic or any others, please share your thoughts with me on &lt;a href="https://twitter.com/EricLGoldstein" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. I would certainly enjoy hearing from you! Happy rendering!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>isomorphic</category>
      <category>hydration</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
