<?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>Forem: Royce Threadgill</title>
    <description>The latest articles on Forem by Royce Threadgill (@jrthreadgill).</description>
    <link>https://forem.com/jrthreadgill</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%2F909237%2Fe2c5eaea-6724-4135-a820-559a33e84618.png</url>
      <title>Forem: Royce Threadgill</title>
      <link>https://forem.com/jrthreadgill</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jrthreadgill"/>
    <language>en</language>
    <item>
      <title>React Server Components Example with Next.js</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Tue, 16 Apr 2024 19:17:12 +0000</pubDate>
      <link>https://forem.com/thegnarco/react-server-components-example-with-nextjs-14i0</link>
      <guid>https://forem.com/thegnarco/react-server-components-example-with-nextjs-14i0</guid>
      <description>&lt;p&gt;React Server Components (oftentimes shortened to &lt;strong&gt;RSC&lt;/strong&gt;) provide us with a fascinating new approach to building React applications.&lt;/p&gt;

&lt;p&gt;That said, my experience while researching this article was &lt;strong&gt;far less rosy&lt;/strong&gt; and &lt;strong&gt;far more expletive laden &lt;/strong&gt;than I expected.&lt;/p&gt;

&lt;p&gt;But now that I have been one acquainted with the RSC, I’d like to share my findings with you so that hopefully your learning journey won’t be quite as painful or confusing.&lt;/p&gt;

&lt;p&gt;In this article, we’ll discuss a range of topics, including:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;What are React Server Components?&lt;/li&gt;
    &lt;li&gt;Can you use React Server Components without Next.js?&lt;/li&gt;
    &lt;li&gt;How do React Server Components work in Next.js?&lt;/li&gt;
    &lt;li&gt;What are the advantages and disadvantages of React Server Components?&lt;/li&gt;
    &lt;li&gt;Are React Server Components production ready?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll also go through a React Server Components example using the Next.js app router. That example will discuss RSC &lt;strong&gt;streaming behavior&lt;/strong&gt; and &lt;strong&gt;client and server component nesting&lt;/strong&gt;. If you’re only interested in the code example, just scroll to the bottom of the article.&lt;/p&gt;

&lt;p&gt;Let’s begin!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are React Server Components?
&lt;/h2&gt;

&lt;p&gt;My favorite definition of RSC comes from &lt;a href="https://github.com/markdalgleish"&gt;Mark Dalgleish&lt;/a&gt; in his 2023 React Advanced conference talk, “&lt;a href="https://youtu.be/36uY-c0E_EQ?si=SipZ6fLWkd0B2pCc"&gt;Simplifying Server Components&lt;/a&gt;”. The way he puts it is that “server components are just virtual DOM over the network”.&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%2F76iasv9he2begq06pppc.jpg" 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%2F76iasv9he2begq06pppc.jpg" alt="Mark Dalgleish provides RSC definition" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What does this mean?&lt;/p&gt;

&lt;p&gt;Let’s say we have a React component called &lt;code&gt;Basic&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Basic&lt;/span&gt; &lt;span class="o"&gt;=&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&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;On the server, that component would be serialized into what’s known as an &lt;strong&gt;RSC payload&lt;/strong&gt;. That would look &lt;em&gt;something &lt;/em&gt;like this in the Next.js server component implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...complicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Next.js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stuff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"__PAGE__"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"$L1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"p"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello, World"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;complicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Next.js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stuff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;down&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That RSC payload would then be streamed down to the client and rendered exactly like any other JSX.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: This explanation gets at the core of React Server Components, but is a little simplified. If you want to learn more about what’s happening under the hood, I highly recommend watching to &lt;a href="https://github.com/dai-shi"&gt;Daishi Kato&lt;/a&gt;’s “&lt;a href="https://youtu.be/_SLh2g5N3mk?si=fSg8799Qm79nsbm3"&gt;Exploring React Server Component Fundamentals&lt;/a&gt;” talk at React Day Berlin 2023.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Seems pretty simple, right? &lt;strong&gt;So why bother with this RSC payload nonsense?&lt;/strong&gt; Why not just render the component to HTML and stream &lt;em&gt;that&lt;/em&gt; down to the client?&lt;/p&gt;

&lt;p&gt;Well, we can! Typically, we would call this &lt;strong&gt;server side rendering&lt;/strong&gt; (SSR).&lt;/p&gt;

&lt;h2&gt;
  
  
  React Server Components vs SSR
&lt;/h2&gt;

&lt;p&gt;We’ve actually been able to serialize React components to HTML for several years now.&lt;/p&gt;

&lt;p&gt;You can find a solid &lt;a href="https://vitejs.dev/guide/ssr"&gt;definition of SSR in the Vite documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SSR specifically refers to front-end frameworks (for example React, Preact, Vue, and Svelte) that support running the same application in Node.js, pre-rendering it to HTML, and finally hydrating it on the client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Put simply, &lt;strong&gt;SSR pre-renders a React component to HTML&lt;/strong&gt;, while &lt;strong&gt;RSC serializes a React component into an RSC payload&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Server components have a couple advantages over traditional SSR. For one, SSR requires a hydration step once the HTML has been sent to the client; the initially loaded page isn’t interactive until the entire JS bundle is downloaded and the entire page is hydrated.&lt;/p&gt;

&lt;p&gt;By comparison, server components require no hydration step, allowing pages to &lt;strong&gt;load faster&lt;/strong&gt; &lt;strong&gt;and become interactive more quickly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The page loading issues raised by SSR can be mitigated by chunking your JavaScript bundle and streaming it to the client, but eventually the entire bundle needs to be downloaded. And that bundle tends to get larger as your application grows.&lt;/p&gt;

&lt;p&gt;Server components, meanwhile, add no additional JavaScript to your bundle. As your application grows, your JavaScript bundle size does not grow with it (assuming you &lt;em&gt;only&lt;/em&gt; use server components).&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: It’s important to note that RSC and SSR aren’t mutually exclusive. You can learn a bit more about this by reading “&lt;a href="https://www.joshwcomeau.com/react/server-components/"&gt;Making Sense of React Server Components&lt;/a&gt;” by Josh Comeau.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Components vs. Client Components
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Basic&lt;/code&gt; component above was a simple example. What happens when we add interactivity to our component?&lt;/p&gt;

&lt;p&gt;Let’s say we add a &lt;code&gt;Counter&lt;/code&gt; to our &lt;code&gt;Basic&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Basic&lt;/span&gt; &lt;span class="o"&gt;=&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&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;When we interact with the &lt;code&gt;Counter&lt;/code&gt;, we would expect the value to increment. In other words, &lt;strong&gt;that component has state&lt;/strong&gt; and must be declared as a client component in our React code.&lt;/p&gt;

&lt;p&gt;How do we represent such a component in a format that can be generated on the server and sent to the client? The answer, as you may have guessed, lies in the &lt;strong&gt;RSC payload&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a bundler supporting React Server Components detects a client component, it adds a reference to that component in the RSC payload – &lt;code&gt;react.client.reference&lt;/code&gt;.&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%2Fyiu8w02aedgtmkppu457.jpg" 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%2Fyiu8w02aedgtmkppu457.jpg" alt="Mark Dalgleish explains RSC client references" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The bundler also generates a manifest that maps the reference ID to the actual client component, which determines what JavaScript must be sent to the client (meaning that, unlike server components, &lt;strong&gt;client components&lt;/strong&gt; &lt;strong&gt;will contribute to your bundle size&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://changelog.com/jsparty/311"&gt;Episode #311 of the Changelog podcast&lt;/a&gt;, &lt;a href="https://github.com/gaearon"&gt;Dan Abramov&lt;/a&gt; said (and I’m paraphrasing a bit here) the RSC payload resulting from such scenarios can be thought of as having a &lt;code&gt;script&lt;/code&gt; tag in place of the client component.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote:&lt;/em&gt; &lt;em&gt;If you’re interested in learning a bit more about how client components are serialized, take a look at “&lt;a href="https://timtech.blog/posts/react-server-components-rsc-no-framework/"&gt;React Server Components, without a framework?&lt;/a&gt;” by &lt;a href="https://github.com/ziir"&gt;Tim Pillard&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As the late Billy Mays would say, “But wait, there’s more!”&lt;/p&gt;

&lt;p&gt;What happens when you nest a server component &lt;em&gt;inside&lt;/em&gt; that client component? As wacky as it sounds, &lt;strong&gt;your bundler recognizes that as a server component&lt;/strong&gt; and serializes it as such in the RSC payload.&lt;/p&gt;

&lt;p&gt;Don’t worry, we’ll get more into server components vs client components in our code example. But first, let’s address the elephant in the room….&lt;/p&gt;

&lt;h2&gt;
  
  
  React Server Components Without Next.js…?
&lt;/h2&gt;

&lt;p&gt;Daishi Kato, creator of &lt;a href="https://github.com/pmndrs/zustand"&gt;Zustand&lt;/a&gt; and &lt;a href="https://github.com/pmndrs/jotai"&gt;Jotai&lt;/a&gt;, says there are a few things we need to support RSC serialization:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;strong&gt;Bundler&lt;/strong&gt; capable of recognizing client components, generating a manifest, and resolving client references on the client&lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;Server&lt;/strong&gt; capable of accepting HTTP requests and responding with (dynamic) RSC payloads&lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;Router&lt;/strong&gt; capable of intelligently serving resources (e.g., don’t render client components that are not useful for a given route)&lt;/li&gt;
    &lt;li&gt;
&lt;strong&gt;SSR&lt;/strong&gt; that works in tandem with React Server Components, largely for initial page load performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So while the RSC concept is fairly straightforward in theory, there are a number of moving parts necessary for implementing RSCs in practice.&lt;/p&gt;

&lt;p&gt;Currently, &lt;strong&gt;Next.js is the only production framework that fully supports RSCs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn’t an accident; when &lt;a href="https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components"&gt;Meta introduced React Server Components&lt;/a&gt;, Dan Abramov explicitly stated that they collaborated with the Next.js team to develop the &lt;a href="https://github.com/facebook/react/tree/main/packages/react-server-dom-webpack"&gt;RSC webpack plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That means you’re &lt;em&gt;mostly&lt;/em&gt; limited to Next.js if you’d like to build an app with RSCs.&lt;/p&gt;

&lt;p&gt;📣 &lt;strong&gt;Important&lt;/strong&gt;: This article was originally written in March 2024. At the very end of that month, &lt;a href="https://redwoodjs.com/blog/rsc-now-in-redwoodjs"&gt;RedwoodJS announced full support for server components&lt;/a&gt;! While there still appear to be some implementation issues, RedwoodJS &lt;em&gt;does&lt;/em&gt; now offer a viable alternative for implementing React Server Components in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  But What If I Really Don’t Like Next.js?
&lt;/h3&gt;

&lt;p&gt;If you’re really against using Next.js, your options are limited and largely experimental.&lt;/p&gt;

&lt;p&gt;The moderators at &lt;a href="https://www.reactiflux.com/"&gt;Reactiflux&lt;/a&gt; pointed me toward &lt;a href="https://waku.gg/"&gt;Waku&lt;/a&gt; (also developed by Daishi Kato) as a potential alternative. However, Daishi explicitly recommends using the framework on non-production projects.&lt;/p&gt;

&lt;p&gt;At Node Conference 2023, Jarred Sumner (creator of &lt;a href="https://bun.sh/"&gt;Bun&lt;/a&gt;) showed a &lt;a href="https://portal.gitnation.org/contents/server-components-with-bun"&gt;demo of server components in Bun&lt;/a&gt;, so there is at least partial support in that ecosystem. The Bun repo provides &lt;a href="https://github.com/oven-sh/bun/blob/7f16164b25a4bebb25532285ee3f25746857c5cf/packages/bun-plugin-server-components/README.md"&gt;bun-plugin-server-components&lt;/a&gt; as the official plugin for server components. And while I haven’t looked at it in-depth, &lt;a href="https://github.com/hex2f/marz"&gt;Marz&lt;/a&gt; claims to be a “React Server Components Framework for Bun”.&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%2Fmzxbt62culxpktr5qmea.jpg" 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%2Fmzxbt62culxpktr5qmea.jpg" alt="Jarred Sumner explains server components with Bun" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Changelog Podcast episode referenced above, Dan Abramov alluded to &lt;a href="https://parceljs.org/"&gt;Parcel&lt;/a&gt; working on RSC support as well. I couldn’t find much to back up that claim aside from a &lt;a href="https://github.com/parcel-bundler/parcel/issues/9050"&gt;GitHub issue discussing directives&lt;/a&gt; and a &lt;a href="https://twitter.com/devongovett/status/1749629092914172388"&gt;social media post by Devon Govett&lt;/a&gt; (creator of Parcel), so I can’t say for sure if Parcel is currently a viable option for developing with RSCs.&lt;/p&gt;

&lt;p&gt;And you can’t use React Server Components with Vite &lt;em&gt;right now&lt;/em&gt;, but &lt;a href="https://github.com/facebook/react/pull/26926"&gt;support is being worked on&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Server Components Example with Next.js
&lt;/h2&gt;

&lt;p&gt;Alright, with that out of the way, let’s get to the code example. Note that we’re using Tailwind for styling just to make the example a bit less ugly; Tailwind has no other bearing on the example.&lt;/p&gt;

&lt;p&gt;For reference, here are our dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"14.1.3"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"autoprefixer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^10.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint-config-next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"14.1.3"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re going to cover a few concepts here, but we’ll start with a React Server Components &lt;code&gt;Suspense&lt;/code&gt; example to demonstrate how payload streaming is handled.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Server Components and &lt;code&gt;Suspense&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;React &lt;code&gt;Suspense&lt;/code&gt; can intelligently display parts of the RSC payload as they are streamed down to the client. If the component is not yet available, &lt;code&gt;Suspense&lt;/code&gt; will fall back to a loading state.&lt;/p&gt;

&lt;p&gt;Let’s say we have a component &lt;code&gt;Multiple&lt;/code&gt;, which has multiple server components that take varying amounts of time to load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/multiple/page.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Multiple&lt;/span&gt; &lt;span class="o"&gt;=&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex min-h-screen flex-col items-center justify-between p-24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex w-1/2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-4xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Multiple&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="nx"&gt;things&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LongLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LongerLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LongestLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Multiple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each server component is wrapped within a &lt;code&gt;Suspense&lt;/code&gt; component, which falls back to “Loading…” text until the server component is available.&lt;/p&gt;

&lt;p&gt;For the purposes of this example, we’ll mock each long running query with a &lt;code&gt;Promise&lt;/code&gt; that resolves after a defined timeout. So each of the server components will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longLoadingFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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="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="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vegeta, what does the scouter say about his power level?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&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="nx"&gt;promise&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;LongLoadingComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&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;longLoadingFetch&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-white border border-white p-4&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;res&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;LongLoadingComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component will have a different timeout value and string to simulate different server components with different query durations.&lt;/p&gt;

&lt;p&gt;Initially, each server component is unavailable and all display the “Loading…” text. Then the quickest component renders, then the second quickest, then the third quickest.&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%2Ft5nykmk73sndvs3ti8xw.gif" 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%2Ft5nykmk73sndvs3ti8xw.gif" alt="React Suspense example" width="1070" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://syntax.fm/show/718/react-server-components"&gt;Episode #718 of the Syntax podcast&lt;/a&gt;, &lt;a href="https://github.com/wesbos"&gt;Wes Bos&lt;/a&gt; pointed out a handy &lt;a href="https://chromewebstore.google.com/detail/rsc-devtools/jcejahepddjnppkhomnidalpnnnemomn"&gt;RSC Devtools Chrome extension&lt;/a&gt; that we can use to better understand what’s going on here.&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%2Fjhrpsenhl2etdxd19j72.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%2Fjhrpsenhl2etdxd19j72.png" alt="RSC Devtools tree" width="269" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that we have three lazily loaded components, which lines up with what we would expect. If we click on the first node, we see the RSC payload and the rendered content.&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%2Fa7jl4fz9ipcvz34fs0dw.jpg" 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%2Fa7jl4fz9ipcvz34fs0dw.jpg" alt="RSC Devtools component" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also note that the page header has already been rendered! The remainder of the RSC payload is streamed down to the client as it becomes available. Importantly, this means &lt;strong&gt;the long-running queries are not blocking page rendering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: If you’d like to learn more about how the RSC payload is streamed to the client, check out “&lt;a href="https://www.builder.io/blog/why-react-server-components#react-server-components-rendering-lifecycle"&gt;Why React Server Components Are Breaking Builds to Win Tomorrow&lt;/a&gt;”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But you may have noticed that this example only deals with server components. Let’s examine a more complex example that involves both client and server components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interleaving Client and Server Components
&lt;/h3&gt;

&lt;p&gt;This is where the fun begins. In this example, we’ll be looking at how to implement some basic search functionality. Let’s take a look at the &lt;code&gt;Search&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/search/page.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;};&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex min-h-screen flex-col items-center justify-between p-24&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex w-1/2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-4xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Search&lt;/span&gt; &lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearcheableToDoList&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchField&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* server component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ToDoList&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;searchParams&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="cm"&gt;/* client component */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterDisplay&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ToDoList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/SearcheableToDoList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/CounterProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve added comments to indicate where there are server and client components. Note that the &lt;code&gt;searchParams&lt;/code&gt; value passed into the &lt;code&gt;Search&lt;/code&gt; component is a &lt;a href="https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional"&gt;Next-specific property&lt;/a&gt; containing the query parameters for the page.&lt;/p&gt;

&lt;p&gt;Let’s briefly discuss what the individual components are doing:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;code&gt;CounterProvider&lt;/code&gt; is a &lt;strong&gt;client component &lt;/strong&gt;providing a context to its children components; the context value is just &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;SearcheableToDoList&lt;/code&gt; is a &lt;strong&gt;client component &lt;/strong&gt;wrapper that uses the above context to increment the counter value on clicking a button; it displays the &lt;code&gt;counterVal&lt;/code&gt;, a button to increment the counter, &lt;em&gt;and its children.&lt;/em&gt;
&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;SearchField&lt;/code&gt; is a &lt;strong&gt;client component&lt;/strong&gt; input that updates the URL query parameters&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;ToDoList&lt;/code&gt; is a &lt;strong&gt;server component&lt;/strong&gt; that reads data from a &lt;code&gt;data.json&lt;/code&gt; file, then filters that data based on the &lt;code&gt;searchParams&lt;/code&gt; retrieved in the &lt;code&gt;Search&lt;/code&gt; parent; it displays the resulting data &lt;em&gt;and its children.&lt;/em&gt;
&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;CounterDisplay&lt;/code&gt; is a &lt;strong&gt;client component&lt;/strong&gt; that displays the counter value from the &lt;code&gt;CounterProviderContext.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Next.js documentation refers to this nested pattern as &lt;a href="https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#interleaving-server-and-client-components"&gt;“interleaving” server and client components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But how is it that we can nest a server component within our client components?&lt;/p&gt;

&lt;p&gt;Recall that React Server Components are just &lt;em&gt;serialized vDOM sent over the network&lt;/em&gt;. So as long as the content in the React tree is serializable, it can be handled without issue by your bundler.&lt;/p&gt;

&lt;p&gt;In this case, the &lt;code&gt;ToDoList&lt;/code&gt; server component is serializable specifically because it is a child of its client component wrapper. Children of a React component are really just props – and &lt;strong&gt;props are serializable&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.thegnar.com/wp-content/uploads/2024/04/interleaved-components-example.gif"&gt;Here’s what the rendered components look like&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a couple other little tidbits we can glean from the interleaved components.&lt;/p&gt;

&lt;h4&gt;
  
  
  You Can Still Use Contexts (Carefully)
&lt;/h4&gt;

&lt;p&gt;In Next.js v13+, all components are server components by default and &lt;strong&gt;cannot &lt;/strong&gt;use state hooks like &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or &lt;code&gt;useContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Client components operate exactly the same as before and &lt;strong&gt;can&lt;/strong&gt; use state hooks. For example:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CounterProviderValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SetStateAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;counterVal&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="na"&gt;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&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="o"&gt;=&amp;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;counterVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCounterVal&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="mi"&gt;0&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCounterVal&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/CounterProviderContext.Provider&lt;/span&gt;&lt;span class="err"&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;We declare &lt;code&gt;CounterProvider&lt;/code&gt; as a client component with the &lt;code&gt;use client&lt;/code&gt; directive at the top of the file. This directive is how the bundler identifies &lt;code&gt;CounterProvider&lt;/code&gt; as a client component and treats it accordingly in the RSC payload.&lt;/p&gt;

&lt;p&gt;We can use &lt;code&gt;useState&lt;/code&gt; in &lt;code&gt;CounterProvider&lt;/code&gt; only because we’ve declared it as a client component.&lt;/p&gt;

&lt;p&gt;Similarly, we can access the &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt; in child components, &lt;strong&gt;but only if those children are client components&lt;/strong&gt;.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SearcheableToDoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&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="o"&gt;=&amp;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;counterVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCounterVal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderContext&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex gap-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-white text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&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;setCounterVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterVal&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Counter&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterDisplay&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-2&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;SearcheableToDoList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;SearcheableToDoList&lt;/code&gt; is a client component, we can access &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt; via &lt;code&gt;useContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same can be said of &lt;code&gt;CounterDisplay&lt;/code&gt;. As long as it is declared as a client component, we can use &lt;code&gt;counterVal&lt;/code&gt; and &lt;code&gt;setCounterVal&lt;/code&gt;. That’s true even when &lt;code&gt;CounterDisplay&lt;/code&gt; is a child of the &lt;code&gt;ToDoList&lt;/code&gt; server component.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CounterDisplay&lt;/span&gt; &lt;span class="o"&gt;=&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;counterVal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterProviderContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;counterVal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;CounterDisplay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  You Can Use Server Code in Server Components
&lt;/h4&gt;

&lt;p&gt;Server components are rendered on the server, so we can use any server side code we'd like! For instance, we can read from the file system in &lt;code&gt;ToDoList&lt;/code&gt; to return the list of filtered to-dos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&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="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// import included for clarity&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToDoListProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToDo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isFavorite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/src/app/data.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&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;parsedJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsedJson&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;headingSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ToDo&lt;/span&gt;&lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headingSearch&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="nx"&gt;res&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;ToDoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ToDoListProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&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="o"&gt;=&amp;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;data&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;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex gap-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col gap-4&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;data&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="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ToDo&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border border-white p-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-2xl&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;))}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ToDoList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;SearchField&lt;/code&gt; updates the query parameters, the &lt;code&gt;searchParams&lt;/code&gt; passed to &lt;code&gt;Search&lt;/code&gt; are updated and passed to &lt;code&gt;ToDoList&lt;/code&gt;. We then search the file system using &lt;code&gt;fs.readFile&lt;/code&gt; and filter out the results based on the provided query parameters.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: This works for our example, but there’s a bit of nuance here with Next.js caching behavior that may cause issues if you try something similar in production. Check out this &lt;a href="https://github.com/vercel/next.js/discussions/47227#discussioncomment-5342500"&gt;GitHub discussion&lt;/a&gt; and the &lt;a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic"&gt;Next.js docs&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After we’ve fetched our data and populated the component, the resulting RSC payload is streamed down to the client. Notably, this means that &lt;strong&gt;JavaScript used within your server component (including imports) does not get added to the bundle on your front-end&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;For completeness, here’s the &lt;code&gt;SearchField&lt;/code&gt; component code:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SearchField&lt;/span&gt; &lt;span class="o"&gt;=&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&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;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePathname&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;searchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearchText&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="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;searchTextTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;searchTextTimeoutRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchTextTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// this is just a debounce so we don't update query params on every keystroke&lt;/span&gt;
    &lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&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="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/?heading=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;searchText&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="nx"&gt;pathname&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTextTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathname&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&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;setSearchText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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;h2&gt;
  
  
  What Are the Advantages of React Server Components?
&lt;/h2&gt;

&lt;p&gt;Using server components means your JavaScript bundle size will not scale with the size of your codebase. Couple this with the fact that server components don’t require hydration and you get a shortened time-to-interactive and a reduction in page load times.&lt;/p&gt;

&lt;p&gt;That reduced bundle size means client machines don’t need to download as much JavaScript. Additionally, executing JavaScript on the server is often more efficient for the end-user than executing it on the client (especially for mobile devices). Both of these points lead to improved user experiences for Next.js v13 and up.&lt;/p&gt;

&lt;p&gt;Another interesting point is that executing fetches on the server can allow developers to more easily leverage caching. Next.js already &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating"&gt;handles caching out-of-the-box&lt;/a&gt; and I’m curious to see if the wider adoption of RSC reduces the need to combine React with solutions like &lt;a href="https://www.apollographql.com/docs/apollo-server/"&gt;Apollo Server&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/react/"&gt;Apollo Client&lt;/a&gt;. While there are other benefits to these tools, RSC could provide similar caching behavior without the need to invest in a GraphQL solution.&lt;/p&gt;

&lt;p&gt;Combining React Server Components and &lt;code&gt;Suspense&lt;/code&gt; (along with traditional SSR) should also go a long way toward &lt;a href="https://web.dev/case-studies/economic-times-inp"&gt;improving interaction to next paint (INP) for Next.js applications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;📣 &lt;strong&gt;Important&lt;/strong&gt;: As state above, RedwoodJS very recently added RSC support. That team also emphasized the overlap in functionality between RSC and GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the Disadvantages of React Server Components?
&lt;/h2&gt;

&lt;p&gt;The main disadvantage of React Server Components, in my mind, is the significant increase in complexity.&lt;/p&gt;

&lt;p&gt;A portion of this complexity should fade as React developers get accustomed to the new patterns necessitated by RSC. But as currently implemented, I &lt;em&gt;do&lt;/em&gt; think RSC reduces the overall ergonomics of React – &lt;strong&gt;especially on any page with multiple nested client/server components and contexts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another disadvantage lies with RSC support. Since Next.js is the only framework supporting React Server Components, &lt;strong&gt;you’re stuck with it for now&lt;/strong&gt;. And since Next.js works best when deployed to Vercel, some might also argue that narrows your choice of hosting providers.&lt;/p&gt;

&lt;p&gt;Outside of JavaScript frameworks, there is a known &lt;a href="https://nextjs.org/docs/app/building-your-application/styling/css-in-js"&gt;issue with CSS-in-JS&lt;/a&gt; libraries including &lt;code&gt;emotion&lt;/code&gt; and &lt;code&gt;styled-components&lt;/code&gt;. That problem isn’t insurmountable, but will complicate incremental adoption for large, established code bases. Poor server component support by other libraries will also have a similar effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are React Server Components Production Ready?
&lt;/h2&gt;

&lt;p&gt;My answer here would be a solid &lt;strong&gt;“yes, but…”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’re using Next.js or plan to use Next.js, then &lt;strong&gt;React Server Components are production ready&lt;/strong&gt;. There are definitely rough edges in the Next.js implementation, but I don’t think that’s a show stopper.&lt;/p&gt;

&lt;p&gt;That being said, I suspect that greenfield projects will have an easier time adopting RSC since they can pick and choose compatible libraries. Established codebases may find it very time consuming to update their existing code to cooperate with RSCs, especially given the dearth of libraries supporting it.&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;em&gt;Sidenote: If you want more insight on this, check out &lt;a href="https://nickmccurdy.com/arewerscyet/"&gt;Are we RSC yet?&lt;/a&gt;.&lt;br&gt;
&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are not using Next.js and do not plan to, I would personally advise against implementing RSC into production code until support has improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Questions About React Server Components
&lt;/h2&gt;

&lt;p&gt;In writing this article, I began forming some questions that I don’t yet have the answer to. These include:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;How much will the combination of &lt;a href="https://youtu.be/qOQClO3g8-Y?si=PkSs-h0D9stWjfvO"&gt;React Forget&lt;/a&gt; and React Server Components improve React performance overall? Will they impact or compliment one another at all?&lt;/li&gt;
    &lt;li&gt;How will React Server Components work with &lt;a href="https://expo.dev/"&gt;Expo&lt;/a&gt; and React Native?&lt;/li&gt;
    &lt;li&gt;How does &lt;a href="https://turbo.build/pack"&gt;Turbopack&lt;/a&gt; fit into this? Will it bring RSC functionality to other frameworks?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you know anything about these topics, please feel free to let me know! I’d love to learn from you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;h2&gt;Further Resources and Special Thanks&lt;/h2&gt;
&lt;/h2&gt;

&lt;p&gt;Thanks for sticking with me until the end of a long article! Here are a few additional resources I came across if you’d like to learn more about React Server Components:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;a href="https://remix.run/blog/remix-v2"&gt;Remix v2&lt;/a&gt; (blog post)&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://vercel.com/blog/understanding-react-server-components"&gt;Understanding React Server Components&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://youtu.be/tJY1zrW35q8?si=T5BfTwMXuEo921NQ"&gt;Qwik vs. React Server Components w/ Miško Hevery: Servers, Signals, and Performance&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.mux.com/blog/what-are-react-server-components"&gt;Everything I wish I knew before moving 50,000 lines of code to React Server Components&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://app-router.vercel.app/context"&gt;Next.js client context demo&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.contentful.com/blog/react-server-components-concepts-and-patterns/"&gt;React Server Components: Concepts and Patterns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d also like to extend my thanks to multiple people who provided feedback and pointed me toward resources while writing this article. Thank you for the help and I hope this was an enjoyable read!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development/react-development-js"&gt;builds React applications&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating Passkey Authentication in a Rails 7 Application</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Wed, 27 Mar 2024 17:39:10 +0000</pubDate>
      <link>https://forem.com/thegnarco/creating-passkey-authentication-in-a-rails-7-application-4a15</link>
      <guid>https://forem.com/thegnarco/creating-passkey-authentication-in-a-rails-7-application-4a15</guid>
      <description>&lt;p&gt;If you're looking to allow your users to log into your Rails 7 app with passkeys, well searched.&lt;/p&gt;

&lt;p&gt;This article is specifically about passkeys, which are a form of passwordless login that replaces passwords with secure, per-device keys (the eponymous passkeys) which are handled by the device's operating system.&lt;/p&gt;

&lt;p&gt;As of this writing, up-to-date Windows, macOS, iOS, and Android all support passkeys. Linux users with the correct browser or browser extension can also use passkeys. &lt;/p&gt;

&lt;p&gt;This widespread support is due to the standard called FIDO2, which covers two components: WebAuthn and CTAP2. WebAuthn handles communication between a relying party (your app) and a credential provider (we'll use Bitwarden's Passwordless.dev), while CTAP2 handles key management and communication between the browser and the operating system. &lt;/p&gt;

&lt;p&gt;We won't need to worry about either of these standards, since using Bitwarden as a passkey provider means we can use their open-source library to handle the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails 7 Passkey Authentication Example
&lt;/h2&gt;

&lt;p&gt;Now that we have a concept for what passkey auth is and how it works, let's implement it in a Rails app. You can find the repo for our example at &lt;a href="https://github.com/JackVCurtis/devise-passkeys-example" rel="noopener noreferrer"&gt;https://github.com/JackVCurtis/devise-passkeys-example&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We are going to implement a passwordless strategy using &lt;a href="https://github.com/heartcombo/devise" rel="noopener noreferrer"&gt;Devise&lt;/a&gt;. For the uninitiated: Devise is an authentication gem commonly used in Rails applications. It provides several off-the-shelf &lt;em&gt;strategies&lt;/em&gt; which abstract the core concepts typically encountered in conventional web authentication. &lt;/p&gt;

&lt;p&gt;The core functionality of Devise focuses on the username/password authentication paradigm. Here we will add a custom strategy to Devise using passkeys rather than username/password.&lt;/p&gt;

&lt;p&gt;As we mentioned earlier, Bitwarden (specifically Passwordless.dev) is a passkey provider which manages both sides of the passkey management flow (WebAuthn and CTAP2). The first step of our process is to create an account and an application with Passwordless.dev. &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%2F0xcvlw3masd0sn4mse0q.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%2F0xcvlw3masd0sn4mse0q.png" alt="Passwordless.dev setup screen"&gt;&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%2Fsytrmg5luij06qgtcxoj.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%2Fsytrmg5luij06qgtcxoj.png" alt="Passwordless.dev dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will be given three values: An API URL, an API private key, and an API public key. Create a &lt;code&gt;.env&lt;/code&gt; file and provide these values:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

BITWARDEN_PASSWORDLESS_API_URL=
BITWARDEN_PASSWORDLESS_API_PRIVATE_KEY=
BITWARDEN_PASSWORDLESS_API_PUBLIC_KEY=


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Set up your database and run the app locally:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

rails db:create
rails db:migrate
rails s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install Devise and associate it with a &lt;code&gt;User&lt;/code&gt; model. I recommend removing the &lt;code&gt;password&lt;/code&gt; field before running the migration; otherwise Devise will create an automatic presence validation on the password, which we will not be using.&lt;/p&gt;

&lt;p&gt;The migration file that Devise generates, after we have removed the &lt;code&gt;password&lt;/code&gt; field, should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# frozen_string_literal: true

class DeviseCreateUsers &amp;lt; ActiveRecord::Migration[7.1]
  def change
    create_table :users, id: :uuid do |t|
   ## Passkey authenticatable
      t.string :email,              null: false, default: ""

   ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      t.string :unconfirmed_email
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at

      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :confirmation_token,   unique: true
  end
end


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install the Bitwarden &lt;a href="https://docs.passwordless.dev/guide/frontend/javascript.html" rel="noopener noreferrer"&gt;Passwordless.dev JS client&lt;/a&gt; using your choice of package manager. The example uses import-maps.&lt;/p&gt;

&lt;p&gt;Add our custom passkey Devise module and strategy under the &lt;code&gt;lib/devise&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lib/devise/models/passkey_authenticatable.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

require Rails.root.join('lib/devise/strategies/passkey_authenticatable')

module Devise  
    module Models  
        module PasskeyAuthenticatable  
            extend ActiveSupport::Concern  
        end  
    end
end


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;lib/devise/strategies/passkey_authenticatable.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module Devise
  module Strategies
    class PasskeyAuthenticatable &amp;amp;lt; Authenticatable
      def valid?
        params[:token]
      end

      def authenticate!
        token = params[:token]
        res = Excon.post(ENV['BITWARDEN_PASSWORDLESS_API_URL'] + '/signin/verify', 
          body: JSON.generate({
            token: token
          }),
          headers: {
            "ApiSecret" =&amp;amp;gt; ENV["BITWARDEN_PASSWORDLESS_API_PRIVATE_KEY"],
            "Content-Type" =&amp;amp;gt; "application/json"
          }
        )

      json = JSON.parse(res.body)
      if json["success"]
        success!(User.find(json["userId"]))
      else
        fail!(:invalid_login)
      end
    end
  end
end

Warden::Strategies.add(:passkey_authenticatable, Devise::Strategies::PasskeyAuthenticatable)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Generate the Devise controller for sessions and configure your routes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

devise_for :users, controllers: {
  sessions: "users/sessions"
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;app/controllers/users/sessions.rb&lt;/code&gt; with the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# frozen_string_literal: true

class Users::SessionsController &amp;lt; Devise::SessionsController
  before_action :configure_sign_in_params, only: [:create]

  protected

  def configure_sign_in_params
    devise_parameter_sanitizer.permit(:sign_in, keys: [:token])
  end
end


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create a Stimulus controller at &lt;code&gt;app/javascript/controllers/passwordless_controller.js&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import { Controller } from "@hotwired/stimulus"
import { Client } from '@passwordlessdev/passwordless-client';


export default class extends Controller {
  static targets = [ "email" ]

  connect() {
    this.client = new Client({ apiKey: window.VAULT_ENV.BITWARDEN_PASSWORDLESS_API_PUBLIC_KEY });
    this.csrf_token = document.querySelector('meta[name="csrf-token"]').content
  }

  async register() {
    const email = this.emailTarget.value
    const { token: registerToken } = await fetch('/api/registrations', {
      method: 'post',
      headers: {
        'content-type': 'application/json'
      },
      body: JSON.stringify({
        email: email,
      })
    }).then(r =&amp;gt; r.json())

    const { token, error } = await this.client.register(registerToken)

    if (token) {
      await this.verifyUser(token)
    }

    if (error) {
      console.log(error)
    }
  }

  async login() {
    // Generate a verification token for the user.
    const { token, error } = await this.client.signinWithAlias(this.emailTarget.value);

    if (token) {
      await this.verifyUser(token)
    }
  }

  async verifyUser(token) {
    const verifiedUser = await fetch('/users/sign_in', {
      method: 'post',
      headers: {
        'content-type': 'application/json'
      },
      body: JSON.stringify({
        token,
        authenticity_token: this.csrf_token
      })
    }).then((r) =&amp;gt; r.json());

    if (verifiedUser.id) {
      window.location.reload()
    }
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And insert the controller in the HTML at &lt;code&gt;app/views/users/new.html.erb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;h1&amp;gt;Sign Up&amp;lt;/h1&amp;gt;

&amp;lt;% if current_user %&amp;gt;
  &amp;lt;h2&amp;gt;You're logged in!&amp;lt;/h2&amp;gt;

  &amp;lt;%= button_to(
          "Log Out",
          destroy_user_session_path,
          method: :delete
        ) %&amp;gt;
&amp;lt;% else %&amp;gt;
  &amp;lt;div data-controller="passwordless"&amp;gt;
    &amp;lt;input data-passwordless-target="email" type="email"&amp;gt;

    &amp;lt;button data-action="click-&amp;gt;passwordless#register"&amp;gt;
      Create Account
    &amp;lt;/button&amp;gt;

    &amp;lt;button data-action="click-&amp;gt;passwordless#login"&amp;gt;
      Log In
    &amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Navigate to your new combined signup/login page and test it out!&lt;/p&gt;

&lt;h2&gt;Contributors:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/JackVCurtis" rel="noopener noreferrer"&gt;Jack Curtis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/taylorkearns" rel="noopener noreferrer"&gt;Taylor Kearns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrthreadgill" rel="noopener noreferrer"&gt;Royce Threadgill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span&gt;Learn more about how The Gnar&lt;/span&gt; &lt;a href="https://www.thegnar.com/software-development/ruby-on-rails-development" rel="noopener noreferrer"&gt;&lt;span&gt;builds Ruby on Rails applications&lt;/span&gt;&lt;/a&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>security</category>
      <category>learning</category>
    </item>
    <item>
      <title>DevSecOps 101: What is DevSecOps?</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Sat, 27 Jan 2024 05:49:10 +0000</pubDate>
      <link>https://forem.com/thegnarco/devsecops-101-what-is-devsecops-31il</link>
      <guid>https://forem.com/thegnarco/devsecops-101-what-is-devsecops-31il</guid>
      <description>&lt;h2&gt;
  
  
  DevSecOps vs. DevOps
&lt;/h2&gt;

&lt;p&gt;Though there is overlap between Development Security Operations (DevSecOps) and Development Operations (DevOps), they are two distinct topics.&lt;/p&gt;

&lt;p&gt;So, what is the difference between DevOps and DevSecOps?&lt;/p&gt;

&lt;p&gt;Let's first start by defining DevOps. Roughly speaking, I see DevOps as &lt;strong&gt;the practice of using automation to turn code (“development”) into deployments on real infrastructure (“operations”) automatically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, we can define Dev_Sec_Ops as the &lt;strong&gt;integration of security tooling into the CI/CD pipeline to enforce security at each stage of the development and release process&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you have a CI/CD pipeline, you’re implementing &lt;strong&gt;DevOps&lt;/strong&gt;. If you run Brakeman during CI/CD, you’re implementing &lt;strong&gt;DevSecOps&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  DevSecOps as Company Culture
&lt;/h2&gt;

&lt;p&gt;For a deeper dive, I really like &lt;a href="https://snyk.io/series/devsecops/"&gt;this description of DevSecOps by Snyk&lt;/a&gt;. It's worth noting that Snyk’s DevSecOps definition  along with &lt;a href="https://www.redhat.com/en/topics/devops/what-is-devsecops"&gt;the one from RedHat&lt;/a&gt; both use the word "culture" in characterizing the practice. &lt;/p&gt;

&lt;p&gt;It's not a mistake or a hyperbole; embracing DevSecOps demands moving beyond the old "set it and forget it" mentality. You are probably &lt;strong&gt;already adopting DevSecOps at some level&lt;/strong&gt;; you just may not be doing a satisfactory job yet.&lt;/p&gt;

&lt;p&gt;Adopting DevSecOps means &lt;strong&gt;embracing the additional responsibility that software security requires&lt;/strong&gt;. It means using - and understanding - the relevant DevSecOps tools for each stage of the security lifecycle.&lt;/p&gt;

&lt;p&gt;It's a bit of a paradigm shift; many software devs still consider security outside of their job description, and fairly so. But as security moves more and more into the software development lifecycle (which is now fairly married to the deployment lifecycle), it is necessary that all parties involved in that lifecycle be conversant with the security implications of their work. Christophe Limpalair at Spacelift &lt;a href="https://spacelift.io/blog/what-is-devsecops"&gt;puts it nicely&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"...most people view security as an annoyance and as something that adds extra friction to the development and deployment process. Instead of accepting that and putting security on the back burner, DevSecOps brings security back to the forefront by saying “you know what, security is critical and must be part of our process, so let’s make it fit in as painlessly as possible."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's an elephant, but once we break it down we should see that each bite is manageable.&lt;/p&gt;

&lt;p&gt;The US Department of Defense (DoD) does a very nice job of visualizing the DevSecOps lifecycle:&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%2F0h51b6r6cnoc922fmatp.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%2F0h51b6r6cnoc922fmatp.png" alt="DevSecOps Distinct Lifecycle Phases and Philosophies" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read more in the &lt;a href="https://dodcio.defense.gov/Portals/0/Documents/Library/DoDEnterpriseDevSecOpsStrategyGuide.pdf"&gt;DoD Enterprise DevSecOps Strategy Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many similar diagrams online, but I find the DoD's to be the most clear and consumable.&lt;/p&gt;

&lt;p&gt;So what do each of these steps mean? Let's break them down a bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan: Threat Analysis
&lt;/h2&gt;

&lt;p&gt;List all assets that need to be protected (user devices, servers, developer devices), the actors they need to be protected from (ransomware gangs, “hacktivists”, advanced persistent threats like North Korea’s Lazarus Group), and the vulnerabilities those actors could exploit (unauthorized data access, server access, cross-site scripting, compromised SSL certificates). &lt;/p&gt;

&lt;p&gt;A threat analysis plan is often articulated in a &lt;a href="https://www.sciencedirect.com/topics/computer-science/threat-analysis"&gt;threat analysis chart&lt;/a&gt; like this one from &lt;a href="https://www.sciencedirect.com/book/9780123704801/network-analysis-architecture-and-design"&gt;Network Analysis, Architecture, and Design&lt;/a&gt;.&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%2Fr1dylqp1i6zdtvkzxm7e.jpg" 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%2Fr1dylqp1i6zdtvkzxm7e.jpg" alt="Thread analysis table example" width="535" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Develop: Secure Coding; Security as Code
&lt;/h2&gt;

&lt;p&gt;Ensure that developers know the best practices around their language and framework (Ruby on Rails, React Native, etc.). Automate your security tooling using infrastructure as code.&lt;/p&gt;

&lt;p&gt;Learn more by reading Snyk’s article on &lt;a href="https://snyk.io/learn/secure-coding-practices/"&gt;secure coding practices&lt;/a&gt; and GitLab’s &lt;a href="https://about.gitlab.com/blog/2020/03/12/how-to-security-as-code/"&gt;security-as-code guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build: Static Application Security Testing (SAST)
&lt;/h2&gt;

&lt;p&gt;Analyze your source code for any recognizable weaknesses with &lt;a href="https://snyk.io/learn/application-security/static-application-security-testing/"&gt;SAST tools&lt;/a&gt;. This is language- and framework-specific. SAST runs as part of your build pipeline. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/rubysec/bundler-audit"&gt;Bundler Audit&lt;/a&gt; (ruby)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brakemanscanner.org/"&gt;Brakeman&lt;/a&gt; (Ruby on Rails)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.npmjs.com/auditing-package-dependencies-for-security-vulnerabilities"&gt;NPM Audit&lt;/a&gt; (JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sobelow.io/"&gt;Sobelow&lt;/a&gt; (Phoenix Framework)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test: Dynamic Application Security Testing (DAST)
&lt;/h2&gt;

&lt;p&gt;Actively scan for exploitable weaknesses. &lt;a href="https://snyk.io/learn/application-security/dast-dynamic-application-security-testing/"&gt;DAST&lt;/a&gt; software runs against a test environment. This is language-agnostic testing, usually for web applications. &lt;a href="https://www.zaproxy.org"&gt;ZAP&lt;/a&gt; is a good example of DAST software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test: Penetration ("Pen") Testing
&lt;/h2&gt;

&lt;p&gt;Bring on dedicated specialists to attempt to hack a test environment with a variety of tools, and provide a report of vulnerabilities after doing so. There are many consulting firms which provide pen test services. &lt;/p&gt;

&lt;p&gt;Snyk’s &lt;a href="https://snyk.io/blog/penetration-testing-for-developers/"&gt;penetration testing guide&lt;/a&gt; is a good introduction to penetration testing concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release: Digitally Sign
&lt;/h2&gt;

&lt;p&gt;Use &lt;a href="https://en.wikipedia.org/wiki/Code_signing"&gt;cryptographic signatures&lt;/a&gt; to verify that source code is &lt;strong&gt;unmodified from the version published&lt;/strong&gt; by the source domain/package maintainer. If someone were to manipulate the file containing your program to include malicious code, this would be detected by a signature verification tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deliver: Secure Transfer
&lt;/h2&gt;

&lt;p&gt;Ensure that your code is deployed via an &lt;strong&gt;encrypted and secure connection&lt;/strong&gt;. Most cloud platforms will do this automatically. &lt;/p&gt;

&lt;p&gt;If using containers, ensure that the container registry and container image is secure using tools like &lt;a href="https://github.com/quay/clair"&gt;Clair&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy: Security Config, Security Scan
&lt;/h2&gt;

&lt;p&gt;Define and configure your infrastructure using infrastructure as code tools like AWS CDK.&lt;/p&gt;

&lt;p&gt;Scan that infrastructure with a tool like &lt;a href="https://github.com/aquasecurity/cloudsploit"&gt;Cloudsploit&lt;/a&gt; (Multicloud), &lt;a href="https://github.com/cyberark/KubiScan"&gt;KubiScan&lt;/a&gt; (Kubernetes), or &lt;a href="https://aws.amazon.com/inspector/"&gt;Amazon Inspector&lt;/a&gt; (AWS). Like SAST tools, the tools you choose will be specific to your platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operate: Security Patch
&lt;/h2&gt;

&lt;p&gt;Continually patch your code to the latest version. Use tools like &lt;a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates"&gt;dependabot&lt;/a&gt; and &lt;a href="https://github.com/rubysec/bundler-audit"&gt;bundler-audit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitor: Security Audit, Security Monitor
&lt;/h2&gt;

&lt;p&gt;Continuously assess the security of your application's data. This step is best considered &lt;strong&gt;after you understand what it is you are monitoring&lt;/strong&gt; (see everything above). This sounds obvious, but plenty of devs hook up a monitoring system without really knowing what is being monitored.&lt;/p&gt;

&lt;p&gt;In addition to monitoring the software processes, a security audit should also review the user processes, i.e. how users access and share data within and outside of the application.&lt;/p&gt;

&lt;p&gt;Shout out to &lt;a href="https://wazuh.com/"&gt;Wazuh&lt;/a&gt; as a comprehensive monitoring/dashboard tool, while AWS has &lt;a href="https://aws.amazon.com/security-hub/"&gt;Amazon Security Hub&lt;/a&gt;. For auditing there’s a wide range of options, but TechTarget’s &lt;a href="https://www.techtarget.com/searchcio/definition/security-audit"&gt;security audit guide&lt;/a&gt; is a good place to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Investing in a legitimate DevSecOps strategy at your organization is certainly no small investment, in time or resources. But it is a very surmountable challenge. &lt;/p&gt;

&lt;p&gt;If we break the process down as above and become comfortable with each step of the process, we can &lt;strong&gt;weave these security practices into the existing fabric of our software lifecycle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if there is any question about whether this venture is worth the time or resources it demands, I would point you to research into the &lt;a href="https://thehackernews.com/2023/12/cost-of-data-breach-report-2023.html?m=1#key-finding-2-using-a-devsecops-approach-deploying-incident-response-teams-and-using-security-and-ai-automation-produced-large-savings"&gt;savings a company will realize&lt;/a&gt; by investing in a DevSecOps strategy.&lt;/p&gt;

&lt;p&gt;If you're interested in jumping into DevOps or DevSecOps, or if you are already on your way and would like some feedback, &lt;a href="https://www.thegnar.com/contact"&gt;shoot us a line&lt;/a&gt;. We'd love to hear from you.&lt;/p&gt;

&lt;p&gt;Contributors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/JackVCurtis"&gt;Jack Curtis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/taylorkearns"&gt;Taylor Kearns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrthreadgill"&gt;Royce Threadgill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development"&gt;builds software&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>cybersecurity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Gnarly Learnings from December 2023</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Thu, 11 Jan 2024 06:01:14 +0000</pubDate>
      <link>https://forem.com/thegnarco/gnarly-learnings-from-december-2023-3mga</link>
      <guid>https://forem.com/thegnarco/gnarly-learnings-from-december-2023-3mga</guid>
      <description>&lt;p&gt;&lt;em&gt;At The Gnar, we’re always reading, watching, and listening in order to keep our skills sharp and our perspectives fresh. Every month, we round up our learnings and publish them in a Gnarly Learnings blog post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In December 2023, we learned about &lt;strong&gt;AI applications in finance roles&lt;/strong&gt;, strategies for &lt;strong&gt;top converting landing pages&lt;/strong&gt;, and &lt;strong&gt;Static Hermes&lt;/strong&gt;. We also pondered how the combination of &lt;strong&gt;Rails and Hotwire&lt;/strong&gt; can be used to replace some of the functionality previously provided only by &lt;strong&gt;React&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business Applications of AI
&lt;/h2&gt;

&lt;p&gt;McKinsey recently published a &lt;a href="https://www.mckinsey.com/capabilities/strategy-and-corporate-finance/our-insights/gen-ai-a-guide-for-cfos"&gt;brief generative AI guide for CFOs&lt;/a&gt; on how and where to integrate gen AI solutions into finance roles.&lt;/p&gt;

&lt;p&gt;The writers posit that generative AI can help automate tedious tasks, augment productivity, and accelerate data extraction, potentially increasing the velocity of innovation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gen AI can be an important tool for value creation. CFOs should strive to be gen AI enablers, not gatekeepers, and make sure that strategically critical initiatives rapidly and continually receive necessary resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meg McFarlane recently published a Think With Google article discussing the benefits of &lt;a href="https://www.thinkwithgoogle.com/intl/en-emea/marketing-strategies/video/youtube-ai-solutions-oak-furnitureland/"&gt;AI-based advertising strategies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a single case study, they found a 36% increase in unique reach, 45% drop in cost, and 30% higher average impression frequency per user by switching YouTube ad strategy to lean more heavily on Google’s AI ad optimization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where in the past we’d have to do a lot of guesswork to determine the ideal rotation between in-stream and bumper ads, we now let the Google AI decide how often each ad was shown and to whom.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Ecommerce Web Design
&lt;/h2&gt;

&lt;p&gt;In a February 2023 podcast, Logan Grasby discusses the design of &lt;a href="https://unofficialshopifypodcast.com/episodes/logan-grasby-landing-pages-v_ph6SOc"&gt;top converting landing pages&lt;/a&gt;. He goes into detail about the anatomy of a landing page, discussing the purpose of each section and tips for improving marketing performance.&lt;/p&gt;

&lt;p&gt;You can find a summarization of his key points starting around &lt;a href="https://unofficialshopifypodcast.com/episodes/logan-grasby-landing-pages-v_ph6SOc/transcript?t=25m30s"&gt;25:30 in the podcast&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Russ Maschmeyer, product lead for spatial commerce at Shopify, discusses a suite of AI tools called &lt;a href="https://help.shopify.com/en/manual/shopify-admin/productivity-tools/shopify-magic"&gt;Shopify Magic&lt;/a&gt; in a &lt;a href="https://practicalai.fm/246#t=11:20"&gt;recent episode of Practical AI&lt;/a&gt;. The tools can help you with a variety of tasks, from generating product descriptions to &lt;a href="https://huggingface.co/spaces/Shopify/background-replacement"&gt;generating product imagery&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;In December of 2023, we came across two sites – &lt;a href="https://www.addifico.com/"&gt;Addifico&lt;/a&gt; and &lt;a href="https://ai-chat.it/en/"&gt;ai-chat&lt;/a&gt; – that present their business and value proposition with eye-catching, interactive websites.&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%2F6blvdes6lpdvs6s1ytcr.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%2F6blvdes6lpdvs6s1ytcr.png" alt="AI-Chat Screenshots" width="800" height="208"&gt;&lt;/a&gt;&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%2Flpf9pi03inxprjy7y9n9.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%2Flpf9pi03inxprjy7y9n9.png" alt="Addifico Screenshots" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile App Development
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hermesengine.dev/"&gt;Hermes&lt;/a&gt;, the JavaScript engine built and maintained by Meta, is already fine-tuned for running fast React Native applications. But in the presentation “&lt;a href="https://youtu.be/q-xKYA0EO-c?si=YcOAE16xz5iycaZa"&gt;Static Hermes: The Next Generation of Hermes&lt;/a&gt;”, Tzvetan Mikov explains how JavaScript can be compiled to achieve huge leaps in performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://youtu.be/q-xKYA0EO-c?si=G_jQWjjURdTcnsPD&amp;amp;t=901"&gt;15:00&lt;/a&gt;] When we started compiling it to native initially, it was the same performance as Hermes…but now as you can see, it is 10x faster when compiled natively…plus we’re certain that there is more headroom. We can probably get to 20x times faster than Hermes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tzvetan reports &lt;a href="https://youtu.be/q-xKYA0EO-c?si=G_jQWjjURdTcnsPD&amp;amp;t=901"&gt;10x faster performance over traditional Hermes&lt;/a&gt; in one benchmark.&lt;/p&gt;

&lt;p&gt;He also emphasizes that native compilation unlocks “the possibility of very efficient native platform integration”, estimating that call overhead could be between &lt;a href="https://youtu.be/q-xKYA0EO-c?si=tuksL6QgWJHnb7fr&amp;amp;t=1155"&gt;15x and 18x lower than JSI when calling a native function&lt;/a&gt;. This “zero-cost FFI” ultimately opens up the possibility to implement platform integrations “entirely in JavaScript” with “practically no cost, just as if we were a C program”.&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%2Fe4vqt8fm2kl6axr7rlq9.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%2Fe4vqt8fm2kl6axr7rlq9.png" alt="Screenshot from Static Hermes: The Next Generation of Hermes" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in learning more about Static Hermes, Tzvetan created a blog post discussing &lt;a href="https://tmikov.blogspot.com/2023/09/how-to-speed-up-micro-benchmark-300x.html"&gt;how to speed up a micro-benchmark by 300x using Static Hermes&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Gaurav Gautam also wrote a blog post about &lt;a href="https://medium.com/@gautam1168/compiling-typescript-to-native-code-0238d69ca582"&gt;compiling TypeScript to native code&lt;/a&gt;, which &lt;a href="https://github.com/facebook/hermes/discussions/1137#discussioncomment-7201539"&gt;Tzvetan discussed in a GitHub thread&lt;/a&gt;. That same GitHub thread yielded this remarkable result from the bunnymark benchmark:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;Runtime
   &lt;/td&gt;
   &lt;td&gt;FPS
   &lt;/td&gt;
   &lt;td&gt;CPU
   &lt;/td&gt;
   &lt;td&gt;GPU
   &lt;/td&gt;
   &lt;td&gt;Memory
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;C
   &lt;/td&gt;
   &lt;td&gt;90
   &lt;/td&gt;
   &lt;td&gt;68%
   &lt;/td&gt;
   &lt;td&gt;84%
   &lt;/td&gt;
   &lt;td&gt;48 MB
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Static Hermes
   &lt;/td&gt;
   &lt;td&gt;90
   &lt;/td&gt;
   &lt;td&gt;70%
   &lt;/td&gt;
   &lt;td&gt;84%
   &lt;/td&gt;
   &lt;td&gt;53 MB
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Tech Insights
&lt;/h2&gt;

&lt;p&gt;The tech world evolves incredibly fast, so it’s important to stay on top of new strategies for building applications.&lt;/p&gt;

&lt;p&gt;It’s our belief that the &lt;a href="https://hotwired.dev/"&gt;Hotwire&lt;/a&gt; family of products, including the well-known &lt;a href="https://turbo.hotwired.dev/"&gt;Turbo&lt;/a&gt; and &lt;a href="https://stimulus.hotwired.dev/"&gt;Stimulus&lt;/a&gt;, provide a compelling alternative to React for many types of applications.&lt;/p&gt;

&lt;p&gt;Learn more by reading our blog post, “&lt;a href="https://www.thegnar.com/blog/ruby-on-rails-vs-react-hotwire-turbo-change-the-equation"&gt;Ruby on Rails vs. React: Hotwire + Turbo Change the Equation&lt;/a&gt;”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gnarly Bytes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL&lt;/strong&gt;: This &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html"&gt;OWASP Cheat Sheet for GraphQL&lt;/a&gt; is a great summary of best practices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elixir&lt;/strong&gt;: &lt;a href="https://notes.club/"&gt;Notesclub&lt;/a&gt; is a collection of Elixir Livebooks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artificial intelligence&lt;/strong&gt;: &lt;a href="https://ollama.ai/"&gt;Ollama&lt;/a&gt; provides a bunch of AI models you can run locally; Fly.io also created a blog post discussing &lt;a href="https://fly.io/blog/scaling-llm-ollama/"&gt;how to run Ollama models on Fly&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruby&lt;/strong&gt;: &lt;a href="https://docs.rubyonjets.com/"&gt;Ruby on Jets&lt;/a&gt; is an interesting Ruby serverless framework for building and deploying APIs to AWS lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Libraries&lt;/strong&gt;: We discovered &lt;a href="https://spotlightjs.com/"&gt;Spotlight&lt;/a&gt;, a library pitched as “Sentry for Development” (the library is created by &lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt;), aims to provide a rich debugging experience during development&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contributors:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ngmaloney"&gt;Nick Maloney&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrthreadgill"&gt;Royce Threadgill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development"&gt;builds software&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>reactnative</category>
      <category>react</category>
      <category>rails</category>
    </item>
    <item>
      <title>Bun JavaScript Runtime First Look</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Tue, 17 Oct 2023 12:00:00 +0000</pubDate>
      <link>https://forem.com/thegnarco/bun-javascript-runtime-first-look-3eon</link>
      <guid>https://forem.com/thegnarco/bun-javascript-runtime-first-look-3eon</guid>
      <description>&lt;p&gt;When I first heard about the Bun project, I honestly thought it was overambitious to the point of obvious failure. You know the projects I’m talking about – the ones that promise the world but never reach fruition, leaving their early adopters to twist in the wind.&lt;/p&gt;

&lt;p&gt;But here we are: &lt;a href="https://bun.sh/blog/bun-v1.0"&gt;Bun 1.0&lt;/a&gt;. And by all accounts, the Bun JavaScript runtime has achieved its original goals.&lt;/p&gt;

&lt;p&gt;In this article, we’re going to discuss Bun’s features, as well as its history and future. Since the Bun team claims that their product is a drop-in replacement for Node.js, we’ll also examine a non-trivial example of replacing Node.js with Bun in a pre-existing project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Bun?
&lt;/h2&gt;

&lt;p&gt;Bun is a new JavaScript runtime developed by a company called &lt;a href="https://github.com/oven-sh"&gt;Oven&lt;/a&gt; (yes, really). The mastermind behind the project is &lt;a href="https://twitter.com/jarredsumner"&gt;Jarred Sumner&lt;/a&gt;, although at this point the company appears to have attracted a small team of developers.&lt;/p&gt;

&lt;p&gt;On an &lt;a href="https://podrocket.logrocket.com/bun"&gt;episode of the PodRocket podcast&lt;/a&gt;, Jarred summarized Bun as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=130"&gt;2:10&lt;/a&gt;] …an all-in-one JavaScript Runtime. It combines a bundler, a transpiler, a package manager, a script runner, all-in-one in addition to being a runtime. And so it comes with a TypeScript transpiler, JSX transpiler. And all of this is quite a bit faster than anything that exists today.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How Fast Is Bun?
&lt;/h2&gt;

&lt;p&gt;To be clear, when Jarred says “quite a bit faster”, he means &lt;strong&gt;many times faster&lt;/strong&gt; than other prominent JavaScript runtimes such as &lt;a href="https://nodejs.org/en"&gt;Node.js&lt;/a&gt; and &lt;a href="https://deno.com/"&gt;Deno&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One benchmark on the &lt;a href="https://bun.sh/"&gt;Bun website&lt;/a&gt; compares the HTTP requests per second processed by Bun, deno.serve(), and Node.js. When comparing Bun vs. Node, Bun processed 5x as many requests per second; when comparing Bun vs. Deno, Bun processed 2x as many requests per second.&lt;/p&gt;

&lt;p&gt;Another benchmark asserts that &lt;code&gt;yarn install&lt;/code&gt; runs 33x slower than &lt;code&gt;bun install&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Yet another benchmark asserts that tests in a Jest/Babel configuration would run 20x slower than Bun’s test runner. &lt;/p&gt;

&lt;p&gt;Of course, we all know that benchmarks can be misleading. I haven’t personally run the benchmarks provided by Bun, nor have I looked at the code to see if they make sense.&lt;/p&gt;

&lt;p&gt;That being said, these claims have been &lt;a href="https://snyk.io/blog/javascript-runtime-compare-node-deno-bun/"&gt;backed up by some third-party testing&lt;/a&gt;. On our end, one of our talented software engineers has already looked into Bun and was very impressed with its speed compared to Node.js (more on his experience later).&lt;/p&gt;

&lt;h2&gt;
  
  
  How Is Bun So Fast?
&lt;/h2&gt;

&lt;p&gt;According to Jarred Sumner, there are really two major contributors to Bun’s comparative speed: (1) use of the &lt;strong&gt;JavaScriptCore engine&lt;/strong&gt; over V8 and (2) use of the &lt;strong&gt;Zig language&lt;/strong&gt; for building the transpiler and runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/javascriptcore"&gt;JavaScriptCore&lt;/a&gt; is the JavaScript engine built by Apple for Safari, while V8 is the engine built by Google for Chromium-based web browsers. I can’t speak much to their relative speed, nor have I heard Jarred or the Bun team discuss the benefits of JavaScriptCore in great detail.&lt;/p&gt;

&lt;p&gt;The choice to use &lt;a href="https://ziglang.org/"&gt;Zig&lt;/a&gt;, on the other hand, seems like a slam dunk for performance. Bun documentation claims the use of &lt;a href="https://bun.sh/docs/cli/run"&gt;Zig results in a 4x faster startup time for Bun vs. Node&lt;/a&gt; when running scripts.&lt;/p&gt;

&lt;p&gt;Frankly, I hadn’t heard of Zig prior to learning about Bun several months ago. It’s a relatively young language, but seems well-suited to building products like Bun based on Jarred’s statements:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=252"&gt;4:12&lt;/a&gt;] Zig is really good for this type of systems programming to do it as quickly as possible while also not going crazy because it's a low level systems' language with manual memory management. By its nature, without garbage collection, things are a little bit harder, but Zig does a really good job of making it easier than it would be otherwise in C.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Is Bun Production Ready?
&lt;/h2&gt;

&lt;p&gt;By releasing version 1.0, the team is declaring that &lt;strong&gt;Bun is production ready&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redcircle.com/shows/javascript-jabber/ep/254b0cc8-9629-4dcd-9866-6c554cd35ed9"&gt;Speaking on the JavaScript Jabber podcast&lt;/a&gt;, Jarred stated there are already “companies using it in production” with a “decent amount of traffic in some cases”.&lt;/p&gt;

&lt;p&gt;That’s not to say that Bun is flawless. The host of that podcast – which aired May 30, 2023, only three months before 1.0 – mentioned that he had encountered several issues when attempting to use Bun.&lt;/p&gt;

&lt;p&gt;Of course, Jarred assured the host that progress was being made on many known issues.&lt;/p&gt;

&lt;p&gt;My personal opinion? Bun is ready for production use, so long as you’re willing to tackle the weird compatibility issues and edge cases that inevitably arise when using bleeding edge technology. &lt;/p&gt;

&lt;p&gt;So the question really should be: do the pros of adopting Bun outweigh these inconveniences?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Switch to Bun?
&lt;/h2&gt;

&lt;p&gt;There are a few benefits to Bun that jump out at me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do you have a giant test suite that takes a long time to run?&lt;/strong&gt; Adopting Bun will reduce the time to run that test suite, which can save CI/CD dollars and developer time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you run serverless JavaScript functions?&lt;/strong&gt; Switching over to Bun will reduce cold start and execution times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you like TypeScript and want it to work out-of-the-box?&lt;/strong&gt; Bun can directly execute &lt;code&gt;.ts&lt;/code&gt; and &lt;code&gt;.tsx&lt;/code&gt; files with no additional configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you want to reduce your dependencies and simplify bundling?&lt;/strong&gt; Bun can probably help. If you like languages such as Go that have rich standard libraries, Bun brings some of that ethos to the JavaScript world by including its own bundler and package manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aside from directly reducing costs via faster execution time, Bun’s performance has the potential to &lt;strong&gt;increase developer productivity&lt;/strong&gt;. By reducing the amount of time that developers spend waiting on test suites to run, for instance, you also reduce the time needed to iterate on those tests.&lt;/p&gt;

&lt;p&gt;And lastly, the improved developer experience of having an all-in-one product shouldn’t be underestimated. How much time have you invested messing with &lt;code&gt;webpack&lt;/code&gt; configurations or troubleshooting a Jest upgrade? Grouping these disparate elements of JavaScript development within a single, batteries-included framework should help mitigate these sorts of problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the Future of Bun?
&lt;/h2&gt;

&lt;p&gt;Jarred envisions a future in which Bun facilitates fast server-side JavaScript rendering. This certainly agrees with the recent shift back to server-side rendering, as embodied by the changes introduced in &lt;a href="https://nextjs.org/blog/next-13#server-components"&gt;Next.js 13&lt;/a&gt; and the creation of &lt;a href="https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components"&gt;React Server Components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Jarred discusses this concept in his PodRocket interview:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=420"&gt;7:00&lt;/a&gt;] Bun will eventually have a production transpiler API/bundling API. And I think what people will do with that is they will actually bundle and transpile in network requests. And so for that, you need it to be really fast. But the reason why I think people will do that is because it's going to enable a lot of new kinds of plugin systems where you have this dynamic code generation and you do server-side JavaScript rendering. Similarly you do server-side HTML rendering. But if you can embed the data…it'll let people delete a bunch of code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This use-case for Bun seems supportive of the so-called &lt;a href="https://www.patterns.dev/posts/islands-architecture"&gt;islands architecture&lt;/a&gt; (a phrase popularized most recently by &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt;). If you’re unfamiliar with this concept, the main gist is: &lt;strong&gt;since web pages are largely static content, only ship JavaScript when and where you need it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More specifically, I can imagine a scenario in which a user submits data to a server and the server responds by creating data-specific JavaScript which is bundled and sent back to the client as part of an interactive page element. &lt;/p&gt;

&lt;p&gt;That sort of client/server interaction ensures you’re only sending the JavaScript you need to the client – and that the JavaScript you send has the smallest footprint possible.&lt;/p&gt;

&lt;p&gt;I don’t think the value proposition for this sort of system is lost on Jarred and Bun, either. In his interview with JavaScript Jabber, Jarred mentions that he aims to eventually offer “really fast edge hosting for Javascript specialized cloud” as a commercial product.&lt;/p&gt;

&lt;p&gt;And in his PodRocket interview, he also highlights that Oven wants to create a version of Bun specifically for edge computing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[&lt;a href="https://podrocket.logrocket.com/bun?t=967"&gt;16:07&lt;/a&gt;] …one of the things we'll focus on is specifically [a] version of Bun for the edge. It will have single file deploys. And it'll do this really efficiently because it'll use a binary bundling format to turn it all into one compact file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So based on these comments, I anticipate we’ll see Oven create something like &lt;a href="https://deno.com/deploy"&gt;Deno Deploy&lt;/a&gt; for the Bun ecosystem in the not-too-distant future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Bun a Drop-In Replacement for Node.js?
&lt;/h2&gt;

&lt;p&gt;Whenever a new project bills itself as a “drop-in replacement” for an existing tool, I’m immediately suspicious. In my experience, there’s usually some big issue that pops up soon after adopting these sorts of solutions.&lt;/p&gt;

&lt;p&gt;Luckily, one of our Gnarnians has already looked into this! &lt;a href="https://github.com/alxjrvs"&gt;Alex Jarvis&lt;/a&gt; recently converted his &lt;a href="https://github.com/RANDSUM/randsum-ts"&gt;randsum-ts&lt;/a&gt; library from Node.js to Bun and generously agreed to share his takeaways from the experience.&lt;/p&gt;

&lt;p&gt;Firstly, let’s establish that his library itself isn’t extremely complicated. Alex states that it “has no databases, no HTTP calls, just logic and exhaustively-thorough TypeScript”. &lt;/p&gt;

&lt;p&gt;But if you look at the library, I’m sure you’ll quickly notice that it’s a pretty far cry from a “hello, world” application. This is a production-ready, pre-existing project, making it a fantastic opportunity for putting Bun’s “drop-in replacement” claims to the test.&lt;/p&gt;

&lt;p&gt;More importantly, this wasn’t just an experiment for Alex. &lt;strong&gt;He saw legitimate benefits in Bun&lt;/strong&gt; and wanted to leverage those benefits in his project:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have played with a number of different workflows and build scripts – &lt;code&gt;rollup&lt;/code&gt; , &lt;code&gt;parcel&lt;/code&gt;, straight-up &lt;code&gt;tsc&lt;/code&gt; – and had lost my head a few times working my way around &lt;code&gt;cjs&lt;/code&gt; vs. &lt;code&gt;esm&lt;/code&gt;. What I wanted was a little existential stability in my workflow – something to put my back against. When I heard of Bun’s built-in (lightning fast) bundler &lt;em&gt;and&lt;/em&gt; saw it operated with first-level TypeScript support, I decided to try my hand at converting from a &lt;code&gt;yarn/node&lt;/code&gt; backed project to being one run on &lt;code&gt;bun&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve lived in the JavaScript world for a while, this is probably a familiar sentiment. My hope is Bun helps to smooth out developer experience by avoiding these sorts of headaches.&lt;/p&gt;

&lt;p&gt;Replacing Node.js with Bun was fairly straightforward, though Alex mentioned it did involve “a small amount of re-architecturing”. To be fair, I expected that changing the JavaScript runtime for an existing project would be an outright nightmare; &lt;a href="https://github.com/RANDSUM/randsum-ts/pull/543"&gt;his experience&lt;/a&gt; seemed positively seamless by comparison.&lt;/p&gt;

&lt;p&gt;He did, however, highlight testing as a pain point during adoption:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The biggest headache was the test suite, where a few useful / mission critical things are not yet implemented when  compared to the modern &lt;code&gt;jest&lt;/code&gt; suite (while I can live without &lt;code&gt;expect.arrayContaining&lt;/code&gt;, it is a slightly more annoying existence) With  some creative dependency injection and well-architected tests, this was easy to get around.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That said, Alex seemed genuinely impressed with the results once he was done. He stated his test suite and build script ran “instantly” after the switch, which serves as yet more validation of Bun’s impressive performance claims.&lt;/p&gt;

&lt;p&gt;Alex additionally pointed out an interesting benefit to Bun’s TypeScript support that hadn’t occurred to me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is already a plugin ecosystem developing (making it easy for my &lt;code&gt;build&lt;/code&gt; script to also spit out a &lt;code&gt;d.ts&lt;/code&gt; file). Being able to REPL one of my TypeScript files natively has really opened up my debugging possibilities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So is Bun a drop-in replacement for Node.js? I’ll let you come to your own conclusion on that. But from my perspective, Alex’s experience is a solid endorsement of that claim.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>bunjs</category>
      <category>typescript</category>
      <category>zig</category>
    </item>
    <item>
      <title>Metaprogramming in Ruby: Beginner Level</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Fri, 30 Dec 2022 22:22:37 +0000</pubDate>
      <link>https://forem.com/thegnarco/metaprogramming-in-ruby-beginner-level-5m4</link>
      <guid>https://forem.com/thegnarco/metaprogramming-in-ruby-beginner-level-5m4</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is the first in a series focused on the application of Ruby metaprogramming. If you’re just starting to learn about metaprogramming, this is a great place to get started. For those who already know the basics, stay tuned for future installments that will cover intermediate and advanced topics.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Metaprogramming is a phrase you’ve probably heard once or twice in your career. Some may have uttered it with reverence and others may have flown into an apoplectic fit of rage at the very mention of it. In this article, we’ll discuss the basics of Ruby metaprogramming so that you can decide for yourself how and when to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby Metaprogramming Questions: What is it?
&lt;/h2&gt;

&lt;p&gt;Generally speaking, metaprogramming is the art and science of &lt;strong&gt;creating a piece of code that generates more code&lt;/strong&gt; for you. This can be useful if you have recurring logic that you want to DRY (i.e., Don't Repeat Yourself) out. Let’s look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Truck&lt;/span&gt;
 &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:is_used&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_used&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_used&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Truck_condition?&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'used'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new'&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sedan&lt;/span&gt;
 &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:is_used&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_used&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_used&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Sedan_condition?&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'used'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new'&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we have two classes, &lt;code&gt;Truck&lt;/code&gt; and &lt;code&gt;Sedan,&lt;/code&gt; with very similar logic but slightly different method names: &lt;code&gt;Truck_condition&lt;/code&gt; and &lt;code&gt;Sedan_condition&lt;/code&gt; (we’re breaking a couple of Ruby naming conventions here for the sake of illustration). Instead of repeating ourselves, we could &lt;em&gt;programmatically&lt;/em&gt; generate these methods using Ruby metaprogramming.&lt;/p&gt;

&lt;p&gt;But why wouldn’t we simply refactor these classes and get the same result by inheriting from a parent class? Well, that leads to our next question….&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby Metaprogramming Questions: When is it Useful?
&lt;/h2&gt;

&lt;p&gt;When you introduce metaprogramming into your code, you start to create complexity that may confuse other developers later on – especially if they didn’t work directly on that code. In most examples like the one given above, you can and should favor simple inheritance.&lt;/p&gt;

&lt;p&gt;That being said, here are a handful of cases in which metaprogramming might come in handy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you’re working with data that doesn’t easily map to a database (e.g., calling a method whose response varies with time)&lt;/li&gt;
&lt;li&gt;If you’re working on a well-aged Rails monolith and you’re worried that refactoring could break something critical in the application&lt;/li&gt;
&lt;li&gt;If you’re purposefully trying to obfuscate the underlying Ruby code for a specific use-case (more on that later)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Metaprogramming is very powerful, but the truth is that it’s often overkill for the task at hand. It’s like when the Ikea manual firmly instructs you to hand-tighten, but you decide to grab your drill driver: We all want to take the opportunity to play with our power tools, but we’ll probably just end up breaking the furniture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby &lt;code&gt;define_method&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of the most important methods in Ruby metaprogramming is &lt;code&gt;define_method&lt;/code&gt;. Here’s a basic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VehicleClass&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_used&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_used&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Truck_condition'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'used'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new'&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_used&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would create a method &lt;code&gt;Truck_condition&lt;/code&gt; on &lt;code&gt;VehicleClass&lt;/code&gt; that would return “used” if &lt;code&gt;is_used == true&lt;/code&gt; and “new” otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby &lt;code&gt;define_method&lt;/code&gt; With Arguments
&lt;/h2&gt;

&lt;p&gt;You can pass arguments to methods created via &lt;code&gt;define_method&lt;/code&gt;. We’ll go over this in more detail in the upcoming example, but here’s an isolated code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Truck_report'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="s2"&gt;"Car is used. Report made for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we create a method &lt;code&gt;Truck_report&lt;/code&gt;. When we call that method, we can pass in a string &lt;code&gt;name&lt;/code&gt; that is added to the response. So calling &lt;code&gt;Truck_report('Alex')&lt;/code&gt; would generate the string, “Car is used. Report made for Alex”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing &lt;code&gt;attr_reader&lt;/code&gt; with &lt;code&gt;define_method&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You may have also noticed that we defined a getter method for the &lt;code&gt;is_used&lt;/code&gt; value. It’s not common to see this kind of syntax because we could use &lt;code&gt;&lt;a href="https://ruby-doc.org/core-2.2.3/Module.html#method-i-attr_reader" rel="noopener noreferrer"&gt;attr_reader&lt;/a&gt;&lt;/code&gt; instead. As a practical example, let’s use &lt;code&gt;define_method&lt;/code&gt; to create our own custom &lt;code&gt;attr_reader&lt;/code&gt; so that we no longer need a getter method in this class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;custom_attr_reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kp"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="nb"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="kp"&gt;attr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VehicleClass&lt;/span&gt;
 &lt;span class="n"&gt;custom_attr_reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:is_used&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_used&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_used&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Truck_condition'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="vi"&gt;@is_used&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'used'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new'&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define &lt;code&gt;custom_attr_reader&lt;/code&gt; on &lt;code&gt;Class&lt;/code&gt;, which all Ruby classes inherit from. Calling &lt;code&gt;custom_attr_reader(:is_used)&lt;/code&gt; within &lt;code&gt;VehicleClass&lt;/code&gt; creates an &lt;code&gt;@is_used&lt;/code&gt; method. This means that &lt;code&gt;VehicleClass.is_used&lt;/code&gt; is now available for all instances of &lt;code&gt;VehicleClass&lt;/code&gt; without the need for a getter method, serving a similar function to &lt;code&gt;attr_reader&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby Metaprogramming Example
&lt;/h2&gt;

&lt;p&gt;With that out of the way, let’s go over a basic metaprogramming example using Ruby 3.1.0 and &lt;code&gt;ActiveSupport::Concern&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this example, we’re creating a &lt;code&gt;VehicleClass&lt;/code&gt; that should have a variety of car-specific methods. These methods will be built programmatically with Ruby &lt;code&gt;define_method&lt;/code&gt; and included in &lt;code&gt;VehicleClass&lt;/code&gt;. To begin, let’s define a “builder” method that will build the “vehicle condition” methods we worked with above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VehicleBuilder&lt;/span&gt;
 &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

 &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_vehicle_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;is_used&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
     &lt;span class="n"&gt;condition_method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_condition"&lt;/span&gt;

     &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition_method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;is_used&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'used'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new'&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VehicleHelper&lt;/span&gt;
 &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

 &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;VehicleBuilder&lt;/span&gt;

 &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;build_vehicle_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="ss"&gt;vehicle_type: &lt;/span&gt;&lt;span class="s1"&gt;'Truck'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;is_used: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;build_vehicle_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="ss"&gt;vehicle_type: &lt;/span&gt;&lt;span class="s1"&gt;'Sedan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;is_used: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VehicleClass&lt;/span&gt;
 &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;VehicleHelper&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;build_vehicle_methods&lt;/code&gt; class will accept &lt;code&gt;vehicle_type&lt;/code&gt; and &lt;code&gt;is_used&lt;/code&gt; arguments. The &lt;code&gt;vehicle_type&lt;/code&gt; argument is used to define the name of the “vehicle condition” method, while &lt;code&gt;is_used&lt;/code&gt;  determines the response from that method.&lt;/p&gt;

&lt;p&gt;We then call &lt;code&gt;build_vehicle_methods&lt;/code&gt; within &lt;code&gt;VehicleHelper&lt;/code&gt;, including our vehicle types and used/new status as arguments. By including &lt;code&gt;VehicleHelper&lt;/code&gt; within our &lt;code&gt;VehicleClass&lt;/code&gt;, we end up with the same methods defined earlier in the article: &lt;code&gt;Truck_condition&lt;/code&gt; and &lt;code&gt;Sedan_condition&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You can easily verify this with an Interactive Ruby session. Simply open your terminal and enter &lt;code&gt;irb&lt;/code&gt;. On your first line, enter &lt;code&gt;require "active_support/concern"&lt;/code&gt;, then copy/paste the above code into your window. Once you’ve done that, create a new instance of &lt;code&gt;VehicleClass&lt;/code&gt; and verify that you can call &lt;code&gt;Truck_condition&lt;/code&gt; and &lt;code&gt;Sedan_condition&lt;/code&gt; on that instance.&lt;/p&gt;

&lt;p&gt;This approach is obviously a more roundabout way of defining those methods. But one advantage here is that you can build out new methods with consistent naming conventions by simply adding a new &lt;code&gt;build_vehicle_methods&lt;/code&gt; call to &lt;code&gt;VehicleHelper&lt;/code&gt;. That would be helpful if you have or plan to have a large number of “vehicle” classes; rather than copy/pasting a bunch of classes like &lt;code&gt;Truck&lt;/code&gt; and &lt;code&gt;Sedan&lt;/code&gt;, you can create them all within &lt;code&gt;VehicleHelper&lt;/code&gt; and have guaranteed consistency.&lt;/p&gt;

&lt;p&gt;And that advantage is compounded as the number of methods and complexity of logic increases. We can demonstrate by adding a few new methods to our &lt;code&gt;VehicleBuilder&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VehicleBuilder&lt;/span&gt;
 &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

 &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_vehicle_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;serial_number&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;is_used&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;damages&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
     &lt;span class="n"&gt;report_method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_report"&lt;/span&gt;
     &lt;span class="n"&gt;condition_method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_condition"&lt;/span&gt;

     &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition_method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;is_used&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'used'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'new'&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report_method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="c1"&gt;# The "condition" method for this vehicle isn't yet defined&lt;/span&gt;
       &lt;span class="n"&gt;condition_attr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition_method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (SN &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;serial_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;condition_attr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Report made for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;damages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;damage&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="n"&gt;damage_method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;vehicle_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_has_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;damage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_damage?"&lt;/span&gt;

       &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;damage_method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="kp"&gt;true&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;VehicleHelper&lt;/span&gt;
 &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

 &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;VehicleBuilder&lt;/span&gt;

 &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;build_vehicle_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="ss"&gt;vehicle_type: &lt;/span&gt;&lt;span class="s1"&gt;'Truck'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;serial_number: &lt;/span&gt;&lt;span class="s1"&gt;'123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;is_used: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;damages: &lt;/span&gt;&lt;span class="sx"&gt;%w[windshield front_passenger_door]&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;build_vehicle_methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="ss"&gt;vehicle_type: &lt;/span&gt;&lt;span class="s1"&gt;'Sedan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;serial_number: &lt;/span&gt;&lt;span class="s1"&gt;'456'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;is_used: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;damages: &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VehicleClass&lt;/span&gt;
 &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;VehicleHelper&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we’re still generating the “condition” methods from above, but we’ve expanded the use of Ruby &lt;code&gt;define_method&lt;/code&gt; to include (1) a “report” method and (2) multiple “damages” methods.&lt;/p&gt;

&lt;p&gt;The “report” methods, &lt;code&gt;Truck_report&lt;/code&gt; and &lt;code&gt;Sedan_report&lt;/code&gt;, will generate report strings similar to those demonstrated earlier in the article. But that string includes the condition of the car – how do we get that condition if the condition method isn’t yet defined? &lt;/p&gt;

&lt;p&gt;Ruby provides a &lt;code&gt;send&lt;/code&gt; method that can address this problem. We can call &lt;code&gt;Truck_condition&lt;/code&gt; and &lt;code&gt;Sedan_condition&lt;/code&gt; from within the report-related &lt;code&gt;define_method&lt;/code&gt; blocks using &lt;code&gt;send(condition_method_name)&lt;/code&gt;. While this particular example is a bit contrived, the ability to call as-of-yet-undefined methods is quite useful for Ruby metaprogramming.&lt;/p&gt;

&lt;p&gt;Finally, the “damages” methods are created by looping over the &lt;code&gt;damages&lt;/code&gt; array. In this example, that creates &lt;code&gt;Truck_has_front_passenger_door_damage?&lt;/code&gt; and &lt;code&gt;Truck_has_windshield_damage?&lt;/code&gt; methods that simply return &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby Metaprogramming Questions: Who Actually Uses This?
&lt;/h2&gt;

&lt;p&gt;Earlier, we briefly touched on the “what” and the “when” of Ruby metaprogramming, but we didn’t discuss the “who”. If this is such a niche strategy, who actually uses it?&lt;/p&gt;

&lt;p&gt;For guidance, we can turn to an oft-cited quote from Tim Peters, a major Python-language contributor, regarding Python metaprogramming: “&lt;em&gt;[Metaclasses] are deeper magic than 99% of users should ever worry about&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;Regardless of the language(s) you work in on a day-to-day basis, metaprogramming is probably not a tool you’ll need to reach for often. There is a notable exception though: metaprogramming is perfect for designing a domain-specific language.&lt;/p&gt;

&lt;p&gt;A domain-specific language (DSL) has its own classes and methods that obfuscate the underlying language they’re built with, reducing complexity and focusing on providing tools to accomplish specific tasks. &lt;a href="https://gradle.org/" rel="noopener noreferrer"&gt;Gradle&lt;/a&gt; is a good example of such a use-case; it takes advantage of &lt;a href="https://groovy-lang.org/metaprogramming.html#_testing_ast_transformations" rel="noopener noreferrer"&gt;Groovy metaprogramming&lt;/a&gt; to deliver a product focused solely on build automation.&lt;/p&gt;

&lt;p&gt;Expanding upon this idea, anyone building a framework will likely find metaprogramming helpful (or even essential). &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails&lt;/a&gt; is one such framework built using the metaprogramming capabilities provided by Ruby. This can be illustrated by the Rails &lt;code&gt;enum&lt;/code&gt; implementation; when you define an enum, the framework provides a variety of helpers out-of-the-box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
 &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:truck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sedan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:minivan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:suv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:delorean&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;suffix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By defining body as an &lt;code&gt;enum&lt;/code&gt;, we automatically gain access to boolean checks like &lt;code&gt;Vehicle.truck?&lt;/code&gt; and status setters like &lt;code&gt;Vehicle.sedan!&lt;/code&gt;. Providing the &lt;code&gt;suffix: true&lt;/code&gt; config option can make these helpers more readable by appending the column name, yielding &lt;code&gt;Vehicle.truck_body?&lt;/code&gt; instead of &lt;code&gt;Vehicle.truck?&lt;/code&gt;.  Database scopes are also generated for our &lt;code&gt;enum&lt;/code&gt;, allowing us to retrieve all truck-body &lt;code&gt;Vehicles&lt;/code&gt; with &lt;code&gt;Vehicle.truck&lt;/code&gt; (and if you’re using Rails 6+, &lt;code&gt;Vehicle.not_truck&lt;/code&gt; will return all &lt;code&gt;Vehicles&lt;/code&gt; that do not have truck bodies).&lt;/p&gt;

&lt;p&gt;This is Ruby metaprogramming at its best: taking Ruby code and augmenting it with human-readable, intuitive helpers for interacting with your database.&lt;/p&gt;

&lt;p&gt;If this article piqued your interest, keep an eye out for the next installment in this series. In our &lt;a href="https://www.thegnar.com/blog/metaprogramming-in-ruby-intermediate-level" rel="noopener noreferrer"&gt;intermediate level post&lt;/a&gt;, we’ll dive into some practical examples of Ruby metaprogramming for those of us not building the newest “blazingly fast” framework.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development/ruby-on-rails-development" rel="noopener noreferrer"&gt;builds Ruby on Rails applications&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
    </item>
    <item>
      <title>Infinite Scroll React Example with TypeScript and NextJS</title>
      <dc:creator>Royce Threadgill</dc:creator>
      <pubDate>Wed, 24 Aug 2022 14:10:00 +0000</pubDate>
      <link>https://forem.com/thegnarco/infinite-scroll-react-example-with-typescript-and-nextjs-1660</link>
      <guid>https://forem.com/thegnarco/infinite-scroll-react-example-with-typescript-and-nextjs-1660</guid>
      <description>&lt;p&gt;For the uninitiated, infinite scroll (otherwise known as endless scroll) refers to a method of automatically loading data when a user scrolls to the bottom of their screen, allowing them to continue browsing content with minimal effort. If you’ve been in the front-end game since the good bad old days when jQuery was bleeding edge, that probably sounds like an irritating feature to build. However, with modern browser APIs and JavaScript libraries like React, it is orders of magnitude less painful to create this effect.&lt;/p&gt;

&lt;p&gt;In this article, we’ll look at a few libraries that can facilitate infinite scroll in React. In case that’s not your style, we’ll also dive into an infinite scroll example that leverages the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;Intersection Observer API&lt;/a&gt; and &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt;, touching on infinite scroll SSR (server-side rendering) tactics that can help with search engine optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Implement Infinite Scroll?
&lt;/h2&gt;

&lt;p&gt;When loading a page, it’s generally preferable to load as little data as possible. This helps the page load faster, which improves user experience and search rankings.&lt;/p&gt;

&lt;p&gt;If you have a large amount of data, you’ll need to split that data up to keep your page loading quickly. That’s where pagination strategies like infinite scroll come in. Pagination refers to the practice of fetching your data in small chunks (or pages), rather than retrieving all of your data at once.&lt;/p&gt;

&lt;p&gt;Many websites implement pagination via page buttons, but infinite scroll is thought to reduce friction since it requires less user interaction. This makes infinite scroll a popular request from clients aiming to improve conversion on their websites. Infinite scroll is especially sought after in e-commerce applications, where getting eyeballs on products is of paramount importance for conversion rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Libraries to Facilitate React Infinite Scroll
&lt;/h2&gt;

&lt;p&gt;If you prefer not to roll your own custom infinite scroll solution, there are many libraries that can simplify building the feature. Here is a non-exhaustive list of options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/civiccc/react-waypoint" rel="noopener noreferrer"&gt;react-waypoint&lt;/a&gt;: As far as ready-made solutions go, this is a personal favorite of mine. The library is simple to use and you can implement infinite scroll with little more than a &lt;code&gt;Waypoint&lt;/code&gt; component and an &lt;code&gt;onEnter&lt;/code&gt; handler. With 4,000 stars on GitHub as of August 2022, react-waypoint is far and away the most popular of the libraries listed here, though it’s also a more general-use library than its counterparts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/onderonur/react-infinite-scroll-hook" rel="noopener noreferrer"&gt;react-infinite-scroll-hook&lt;/a&gt;: This is another library that stood out from the crowd. I haven’t personally used it, but it seems to be more actively maintained than react-infinite-scroll-component. On top of that, it happens to weigh in at a paltry 1.8 kB minified bundle size, &lt;a href="https://bundlephobia.com/package/react-infinite-scroll-hook@4.0.3" rel="noopener noreferrer"&gt;according to bundlephobia&lt;/a&gt;, which appeals to the minimalist in me.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ankeetmaini/react-infinite-scroll-component" rel="noopener noreferrer"&gt;react-infinite-scroll-component&lt;/a&gt;: I found this seemingly popular component in the course of my research for this article. I haven’t used it myself and can’t vouch for it, but it kept popping up so I felt I should add it to this list. It’s worth noting that as of August 2022, this library has 114 open GitHub issues; compared against react-infinite-scroll-hook (2 open issues) and react-waypoint (54 open issues), that’s a relatively high number of problems and could be cause for concern.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Custom Infinite Scroll with TypeScript and NextJS
&lt;/h2&gt;

&lt;p&gt;For those that require (or desire) a custom solution, have no fear: it is surprisingly straightforward to DIY your own infinite scroll feature.&lt;/p&gt;

&lt;p&gt;To help you get started, we’ll be guiding you through an example app that uses React, TypeScript, and NextJS. If you’re unfamiliar with NextJS, you can think of it as a specialized framework built on top of React itself. Don’t worry – a custom infinite scroll React solution is almost identical to a custom infinite scroll NextJS solution. The main reason I chose NextJS for this example was simply to highlight the interaction between server side rendering, infinite scroll, and search engine optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting the Stage for our Infinite Scroll Example
&lt;/h3&gt;

&lt;p&gt;Our sample app is a blog. When a user navigates to the home page of our app, they’re shown a set of blog posts; you can think of this as “page 1” of the blog. When a user reaches the bottom of the home page, more blog posts are loaded via an API call. It looks something like this:&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%2Fan362ji3ber0t56z438h.jpg" 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%2Fan362ji3ber0t56z438h.jpg" alt="Infinite scroll sample app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Load the Initial Data
&lt;/h3&gt;

&lt;p&gt;In this example, we’re going to use the &lt;code&gt;getServerSideProps&lt;/code&gt; method from NextJS to get our initial data, then add that initial posts data to our Home component. After page 1 is loaded, we’ll make additional API calls and add the retrieved data to a &lt;code&gt;dynamicPosts&lt;/code&gt; array.&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;// index.tsx (your home page)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HomeProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;export&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;getServerSideProps&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// creates a unique ID for the post&lt;/span&gt;
     &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Blog post 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Possimus quae numquam repudiandae ab asperiores exercitationem nulla, enim debitis necessitatibus quaerat incidunt nesciunt. Soluta sapiente quisquam magni, quas odit tempora ullam!&lt;/span&gt;&lt;span class="dl"&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;// more data here&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
   &lt;span class="p"&gt;},&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;Home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextPage&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HomeProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;posts&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;hasDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;dynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useInfiniteScroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&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="nc"&gt;HomePage&lt;/span&gt;
     &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hasDynamicPosts&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;dynamicPosts&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLastPage&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;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This essentially means that page 1 of our blog is server-side rendered and should be accessible to search engine crawlers, while all additional data is retrieved client-side. You can use this hybrid infinite scroll SSR strategy if you want search engines to read the first set of data loaded on your infinite scroll website. &lt;/p&gt;

&lt;p&gt;If none of that matters to you, you can think of this as a simple API call. Just remember that the data retrieved via that first client-side API call likely won’t be accessible to search engines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Your Custom Hook
&lt;/h3&gt;

&lt;p&gt;You probably noticed in the above example that there’s a custom hook called &lt;code&gt;useInfiniteScroll&lt;/code&gt;. The vast majority of the logic governing infinite scroll behavior in this example can be found within that hook.&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;// useInfiniteScroll.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UseInfiniteScroll&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;hasDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;dynamicPosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
 &lt;span class="nl"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useInfiniteScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;UseInfiniteScroll&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLoading&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;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPage&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="mi"&gt;1&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;hasDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHasDynamicPosts&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;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BlogPost&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="nx"&gt;posts&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;isLastPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLastPage&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;const&lt;/span&gt; &lt;span class="nx"&gt;observerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;loadMoreTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt; &lt;span class="o"&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;loadMoreTimeoutRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NodeJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadMoreTimeout&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;handleObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setIsLoading&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;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadMoreTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="c1"&gt;// this timeout debounces the intersection events&lt;/span&gt;
       &lt;span class="nx"&gt;loadMoreTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resp&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="nf"&gt;setPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;newPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts&lt;/span&gt;&lt;span class="dl"&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;newPosts&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&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;newDynamicPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;dynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;newPosts&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
             &lt;span class="nf"&gt;setDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="nf"&gt;setIsLastPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;total&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
             &lt;span class="nf"&gt;setHasDynamicPosts&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;setIsLoading&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="p"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;});&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loadMoreTimeoutRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dynamicPosts&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;loadMoreCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLDivElement&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;isLoading&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;observerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;option&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IntersectionObserverInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;rootMargin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
     &lt;span class="nx"&gt;observerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleObserver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;option&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;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;observerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;handleObserver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&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="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;hasDynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;dynamicPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&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;The &lt;code&gt;loadMoreCallback&lt;/code&gt; method is what we’ll use to determine if a user has “intersected” a given element. As you may have guessed, the magic browser API helping us do that is called “Intersection Observer”. We pass a handler method &lt;code&gt;handleObserver&lt;/code&gt; and initialization options to &lt;code&gt;IntersectionObserver&lt;/code&gt;, which will handle emitted events. If the user is intersecting our target element, then &lt;code&gt;target.isIntersecting&lt;/code&gt; is true and an API call is made to retrieve more data.&lt;/p&gt;

&lt;p&gt;Please note that the aforementioned API call is wrapped in a timeout. Intersection Observer may fire multiple intersecting events when the user reaches the target, so we want to debounce those events to avoid making unnecessary calls to our API. We do that by only executing the API call if no other intersecting events have occurred within the last 500 milliseconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Your Intersecting Component
&lt;/h3&gt;

&lt;p&gt;Now you’re probably wondering how we define where the intersection target is. We do that by adding the &lt;code&gt;loadMoreCallback&lt;/code&gt; method as a ref to our desired component. Importantly, we’ll also re-render the intersecting component after new data is loaded to ensure that our intersection target moves along with the bottom of the page.&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;// Loader.tsx&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LoaderProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
 &lt;span class="nx"&gt;UseInfiniteScroll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isLoading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isLastPage&lt;/span&gt;&lt;span class="dl"&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;LoaderProps&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;isLoading&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="p"&gt;&amp;gt;&lt;/span&gt;Loading...&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLastPage&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="p"&gt;&amp;gt;&lt;/span&gt;End of content&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="k"&gt;return&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;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;load more callback&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;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then add that component below our already-loaded content, so that the Intersection Observer will fire an intersection event at the bottom of the page.&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;// HomePage.tsx&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;HomePageProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
 &lt;span class="nx"&gt;UseInfiniteScroll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isLoading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isLastPage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HomePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HomePageProps&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;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;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&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="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Infinite Scroll Demo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;It is a very nice app&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;posts&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;post&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blogPost&lt;/span&gt;&lt;span class="si"&gt;}&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;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://picsum.photos/500"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"random image"&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="nt"&gt;h2&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;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&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;h2&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;p&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;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&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;&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="nt"&gt;div&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="nc"&gt;Loader&lt;/span&gt;
       &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
       &lt;span class="na"&gt;isLastPage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLastPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
       &lt;span class="na"&gt;loadMoreCallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loadMoreCallback&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="nt"&gt;main&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;h2&gt;
  
  
  Wrapping Up Our React Infinite Scroll Example
&lt;/h2&gt;

&lt;p&gt;That’s it! With a couple of components and a custom hook, you can create your own infinite scrolling effect with React, TypeScript, and NextJS.&lt;/p&gt;

&lt;p&gt;Given its ubiquity in consumer software (e.g., social media sites), infinite scrolling has become the go-to pagination strategy for many companies. So if you’ve been lucky enough to dodge infinite scroll so far, I wouldn’t bet on that trend continuing. When you’re inevitably asked to add this feature to an app, I hope this guide can be of help.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development/react-development-js" rel="noopener noreferrer"&gt;builds React applications&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
