<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Viktor Lázár</title>
    <description>The latest articles on DEV Community by Viktor Lázár (@lazarv).</description>
    <link>https://dev.to/lazarv</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%2F1115191%2Fd5ca33e8-94a4-4cdf-9452-400c95556d9c.jpeg</url>
      <title>DEV Community: Viktor Lázár</title>
      <link>https://dev.to/lazarv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lazarv"/>
    <language>en</language>
    <item>
      <title>The "use client" Tax</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Tue, 28 Apr 2026 17:16:59 +0000</pubDate>
      <link>https://dev.to/lazarv/the-use-client-tax-1ed0</link>
      <guid>https://dev.to/lazarv/the-use-client-tax-1ed0</guid>
      <description>&lt;p&gt;&lt;em&gt;Why React Server Components force small interactive ideas into file-sized boundaries — and why that boundary should be lexical instead.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is a moment that every developer who tries React Server Components hits, usually within their first hour. They write a server component. It fetches some data. It renders a list. Beautiful. Then they want a button that toggles a filter, and the compiler stops them: "you can't use &lt;code&gt;useState&lt;/code&gt; here." So they cut the interactive piece out, paste it into a new file, sprinkle &lt;code&gt;"use client"&lt;/code&gt; at the top, import it back into the parent, and move on.&lt;/p&gt;

&lt;p&gt;A week later their &lt;code&gt;components/&lt;/code&gt; directory looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;components/
├── product-list.tsx
├── product-list-filter.tsx
├── product-list-filter-input.tsx
├── product-list-sort.tsx
├── product-list-sort-dropdown.tsx
├── product-card.tsx
├── product-card-actions.tsx
├── product-card-favorite-button.tsx
└── product-card-quantity-stepper.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nine files for one product list. Each one a thin wrapper. Each one with two or three lines of real logic. Each one named with an increasingly desperate suffix because the original &lt;code&gt;Filter&lt;/code&gt; already exists three directories up.&lt;/p&gt;

&lt;p&gt;This is the "use client" tax, and it is real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the tax comes from
&lt;/h2&gt;

&lt;p&gt;The directive is not arbitrary. &lt;code&gt;"use client"&lt;/code&gt; marks a module boundary that the bundler uses to split graphs: everything reachable from a &lt;code&gt;"use client"&lt;/code&gt; entry becomes part of the client bundle; everything else stays on the server. The directive has to live at the top of a file because that is the granularity the bundler operates on. Modules in, modules out.&lt;/p&gt;

&lt;p&gt;That works fine in theory. In practice it forces a one-to-one correspondence between &lt;em&gt;interactive concerns&lt;/em&gt; and &lt;em&gt;files on disk&lt;/em&gt;, and interactive concerns are not file-sized. They are paragraph-sized. A "favorite" button that toggles state is not a module — it is two lines inside the card that displays the product. But the runtime can't see those two lines unless you lift them into their own module, give them a name, export them, import them back, and pass props across the boundary.&lt;/p&gt;

&lt;p&gt;The result is a particular kind of friction that compounds:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File sprawl.&lt;/strong&gt; Trivial widgets become trivial files. Most of the file is the import header.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Naming fatigue.&lt;/strong&gt; Every extracted leaf needs a name. Names that were unique in their lexical scope are no longer unique once they live in a flat directory. You end up with &lt;code&gt;ProductCardFavoriteButtonInner&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lost colocation.&lt;/strong&gt; A server function that writes to the database and the form that calls it now live in two files. The relationship between them survives only as an import statement. To understand the feature you alt-tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Indirection without abstraction.&lt;/strong&gt; Each extracted client component is a wrapper that accepts everything the parent had in scope, as props. You are manually performing closure conversion — by hand, every time, with no help from the compiler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compositions you can't write.&lt;/strong&gt; The pattern that hurts most is the one you cannot express at all: a server function that computes some data and &lt;em&gt;returns&lt;/em&gt; a small interactive component bound to that data. You cannot do this in standard RSC, because the client component has to be a separate module, which means it cannot close over server-side values. You always end up exporting the client component, exporting the data fetch, and re-assembling them at the call site. The expression you wanted to write — a factory — is not available to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the pain
&lt;/h2&gt;

&lt;p&gt;Here is what a real fragment looks like under the current rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// product-card.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FavoriteButton&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;./product-card-favorite-button&lt;/span&gt;&lt;span class="dl"&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;ProductCard&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FavoriteButton&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&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="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isFavorite&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;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// product-card-favorite-button.tsx&lt;/span&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toggleFavorite&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;./product-actions&lt;/span&gt;&lt;span class="dl"&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;function&lt;/span&gt; &lt;span class="nf"&gt;FavoriteButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initial&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;favorite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFavorite&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="nx"&gt;initial&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;button&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="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="nf"&gt;setFavorite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;favorite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;toggleFavorite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&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;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;favorite&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;★&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;☆&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// product-actions.ts&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&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;toggleFavorite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three files for a star button. Two of them exist purely as plumbing for the directive system. The actual interesting code — eight lines of state and a database write — is buried under thirty lines of imports, exports, and prop-passing.&lt;/p&gt;

&lt;p&gt;This is what people mean when they say RSC is heavy. It is not the data fetching. It is not the streaming. It is this: the directive system asks you to manually re-architect every interactive idea into a multi-file module graph, and it does so for the smallest possible units of behavior.&lt;/p&gt;

&lt;p&gt;Zoom out one level and the same pressure exists at the project boundary: modern frontend frameworks force entire micro-apps to be scaffolded across directory trees, config files, and &lt;code&gt;node_modules&lt;/code&gt; for the same kind of mechanical reason — tooling that operates at a coarser unit than the developer's idea. I covered that version of the problem in &lt;a href="https://dev.to/lazarv/the-forgotten-joy-of-node-appjs-5761"&gt;The Forgotten Joy of &lt;code&gt;node app.js&lt;/code&gt;&lt;/a&gt;. The fix is structurally the same as the one proposed below: stop letting the file system be the unit of expression.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint is in the tool, not in the model
&lt;/h2&gt;

&lt;p&gt;Here is the part that is worth saying out loud: the file-level restriction is a property of how bundlers were built, not a property of what the directive &lt;em&gt;means&lt;/em&gt;. &lt;code&gt;"use client"&lt;/code&gt; is asserting that a piece of code runs on the client and must be serialized across a runtime boundary. That assertion is perfectly meaningful at any function scope. It only has to live at the top of a file because that is what the bundler can see.&lt;/p&gt;

&lt;p&gt;A compiler that knows about RSC directives can do better. Given a server module that contains a nested function marked &lt;code&gt;"use client"&lt;/code&gt;, it can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the nested function and the variables it captures from its lexical scope.&lt;/li&gt;
&lt;li&gt;Lift the function into a synthetic module that the bundler treats exactly like a regular &lt;code&gt;"use client"&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;Replace the original definition with a reference to the lifted module.&lt;/li&gt;
&lt;li&gt;Inject the captured variables as props at the call site.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The developer wrote one file. The bundler sees the module graph it needs. Nothing about the underlying RSC contract changes — the same serialization rules apply, the same boundary is enforced — but the file system stops being the unit of expression. The function does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this should look like
&lt;/h2&gt;

&lt;p&gt;Imagine writing the favorite button like this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&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;ProductCard&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FavoriteButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;favorite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFavorite&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="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isFavorite&lt;/span&gt;&lt;span class="p"&gt;);&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;toggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggleFavorite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&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="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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="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="nf"&gt;setFavorite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;favorite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&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;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;favorite&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;★&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;☆&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FavoriteButton&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;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file. Server fetch, client interaction, server function — colocated, in the order you would read them, sharing a closure. The compiler does the lifting; the captured &lt;code&gt;product&lt;/code&gt; becomes a prop on the synthesized client module; the inner &lt;code&gt;"use server"&lt;/code&gt; function becomes a bound server function with the right scope. Server → client → server nesting works recursively because the same extraction pass runs until no nested directives remain.&lt;/p&gt;

&lt;p&gt;This is what a real RSC ergonomic story looks like. Not a new mental model — the same one — just expressed at the granularity humans actually think in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this should be a standard feature
&lt;/h2&gt;

&lt;p&gt;The technique is not exotic. It is closure conversion, a transform compilers have been doing since the seventies. The hard part is wiring it into the RSC plugin chain so that virtual modules generated for inline directives flow through the same client/server graph the rest of the system already uses. That is engineering, not research.&lt;/p&gt;

&lt;p&gt;There is no fundamental reason an RSC-capable runtime cannot support this. The directive system is already a contract between the developer and the compiler; expanding it to cover function scopes in addition to module scopes does not change serialization, bundling, streaming, or the security boundary. It only changes where the developer is allowed to write the directive.&lt;/p&gt;

&lt;p&gt;If you are building an RSC runtime: pick this up. If you are using one that does not have it: ask for it. A "use client" file is not a feature. It is a workaround for a constraint we no longer need to accept.&lt;/p&gt;

&lt;p&gt;The point of RSC was to let us put server logic and client logic next to each other. The directive system, taken at face value, does the opposite: it forces them apart, file by file, until your repository is ninety percent wrappers. We can fix this. It is time to make the fix standard.&lt;/p&gt;

</description>
      <category>rsc</category>
      <category>react</category>
      <category>bundler</category>
      <category>compiler</category>
    </item>
    <item>
      <title>The Forgotten Joy of `node app.js`</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Tue, 28 Apr 2026 17:16:40 +0000</pubDate>
      <link>https://dev.to/lazarv/the-forgotten-joy-of-node-appjs-5761</link>
      <guid>https://dev.to/lazarv/the-forgotten-joy-of-node-appjs-5761</guid>
      <description>&lt;p&gt;There used to be a moment, ten years or so ago, when you could go from "I have an idea" to "I have a running web server" in about thirty seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was the whole thing. One file. One command. You could paste it into a Slack message. You could drop it in a Gist and someone could run it. A tiny webhook receiver, a debug dashboard, an internal tool, a stub API — the entire project lived in a single buffer in your editor.&lt;/p&gt;

&lt;p&gt;Then frontend frameworks happened, and somewhere along the way we collectively decided that "starting a new project" meant something else entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scaffold tax
&lt;/h2&gt;

&lt;p&gt;Today, the canonical first step in starting a new app is no longer writing code. It is running a command that writes code &lt;em&gt;for&lt;/em&gt; you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest my-app
npx create-react-app my-app
npm create vite@latest
npx create-remix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What comes back is not a file. It is a &lt;em&gt;tree&lt;/em&gt;. Configuration files for tooling you have not yet decided to use. A &lt;code&gt;pages/&lt;/code&gt; or &lt;code&gt;app/&lt;/code&gt; directory with conventions you must learn before you can write a single line. A &lt;code&gt;tsconfig.json&lt;/code&gt; you did not write. ESLint rules. Prettier rules. A &lt;code&gt;.gitignore&lt;/code&gt;. A &lt;code&gt;README.md&lt;/code&gt; describing the scaffold itself. A &lt;code&gt;package.json&lt;/code&gt; with twelve dependencies and four scripts you did not pick.&lt;/p&gt;

&lt;p&gt;And, critically, there is no path &lt;em&gt;back&lt;/em&gt; to a single file. The scaffold is the unit of starting. There is no &lt;code&gt;framework dev ./App.jsx&lt;/code&gt;. There is only &lt;code&gt;framework new my-project&lt;/code&gt;, which produces forty files, of which you will edit two.&lt;/p&gt;

&lt;p&gt;This is fine when you are starting a real product. It is absurd when you are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we lost
&lt;/h2&gt;

&lt;p&gt;The single-file app is not a relic of a less mature ecosystem. It is a fundamentally different &lt;em&gt;mode&lt;/em&gt; of working — one the modern frontend toolchain has quietly priced out of existence.&lt;/p&gt;

&lt;p&gt;Specifically, we lost:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The throwaway.&lt;/strong&gt; The five-minute hack to verify that an idea works. The "let me just see what this looks like rendered" experiment. With a scaffold, the cost of starting is high enough that you don't bother. You either pollute an existing big project, or you open the browser DevTools console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The teaching artifact.&lt;/strong&gt; A blog post used to be able to say &lt;em&gt;here, run this file&lt;/em&gt;. Now it says &lt;em&gt;clone this repo&lt;/em&gt;. The reader is no longer reading code; they are operating a project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The micro-app.&lt;/strong&gt; The three-route admin tool. The internal status page. The webhook that posts a Slack message. Things that should be one file are now twenty, because the framework demands it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The shareable Gist.&lt;/strong&gt; I cannot send you a single &lt;code&gt;.jsx&lt;/code&gt; file and have you run it. I have to send you a repository — or a CodeSandbox URL, which is its own confession that the local toolchain has gotten too heavy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The curl-and-run.&lt;/strong&gt; Plain Node lets you stream a program straight from a URL into the runtime, no file on disk: &lt;code&gt;curl https://gist.githubusercontent.com/.../app.js | node&lt;/code&gt;. No clone, no install, no project to set up. The source travels over the wire, lands in the interpreter, runs. The same pattern should work for a single-file frontend app — &lt;code&gt;curl https://.../App.jsx | npx some-framework dev -&lt;/code&gt; — and the fact that this is &lt;em&gt;unimaginable&lt;/em&gt; today is the most concrete possible measurement of how heavy "starting a frontend app" has become. We have a JavaScript-shaped hole in our shells that the language used to fit through.&lt;/p&gt;

&lt;p&gt;There is a fractal version of this same pain one level down. Even &lt;em&gt;inside&lt;/em&gt; a project, modern React's &lt;code&gt;"use client"&lt;/code&gt; directive forces single features to be sharded across multiple files for purely mechanical reasons — the same disease, at smaller scale. I wrote about that version separately in &lt;a href="https://dev.to/lazarv/the-use-client-tax-1ed0"&gt;The "use client" Tax&lt;/a&gt;. What follows here is the project-level shape of the same problem: even when the whole app &lt;em&gt;should&lt;/em&gt; be one file, you are not allowed to write it that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the fix
&lt;/h2&gt;

&lt;p&gt;Imagine, for a second, that this just worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx next dev ./App.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file. One command. The framework picks it up, runs it, hot-reloads it, serves it. No &lt;code&gt;next.config.js&lt;/code&gt;, no &lt;code&gt;pages/&lt;/code&gt;, no &lt;code&gt;app/&lt;/code&gt;, no &lt;code&gt;package.json&lt;/code&gt;. If you decide later that you want a real project, you make the directory, add the config, split the file. The framework grows with you instead of demanding everything upfront.&lt;/p&gt;

&lt;p&gt;The technology to do this is not hard. Frameworks already build on dev servers — Vite, esbuild, Turbopack — that can resolve and bundle a single entry point. The framework conventions (file-based routing, layouts, server components) are conventions &lt;em&gt;over&lt;/em&gt; the bundler, not &lt;em&gt;replacements&lt;/em&gt; for it. There is no fundamental reason a framework's CLI cannot accept a path to a &lt;code&gt;.jsx&lt;/code&gt; file and Just Work, with the conventions kicking in only once you opt into a directory layout.&lt;/p&gt;

&lt;p&gt;The reason it doesn't work is not technical. It's cultural. We have decided, somewhere along the way, that &lt;em&gt;the project&lt;/em&gt; is the unit of frontend code, and the file is merely an implementation detail. Backend frameworks never made that mistake. You can still write a fifteen-line &lt;code&gt;server.js&lt;/code&gt; and run it. You can still write a Flask app in one file. You can still put a Go HTTP handler in &lt;code&gt;main.go&lt;/code&gt; and ship it. Scaffolds are offered as a convenience, not enforced as a precondition.&lt;/p&gt;

&lt;p&gt;Frontend should be no different.&lt;/p&gt;

&lt;h2&gt;
  
  
  One file in, one file out
&lt;/h2&gt;

&lt;p&gt;The single-file dev story is only half of the picture. The other half is what comes out when you build.&lt;/p&gt;

&lt;p&gt;Today, building a frontend project produces another tree. A &lt;code&gt;.next/&lt;/code&gt; directory. A &lt;code&gt;dist/&lt;/code&gt; directory. A &lt;code&gt;.output/&lt;/code&gt; directory. Hundreds of chunked JavaScript files, manifests, server bundles, client bundles, route maps — and a &lt;code&gt;node_modules&lt;/code&gt; you must ship alongside it, or carefully fold into the deployment artifact. Running the result usually means another framework-specific command (&lt;code&gt;next start&lt;/code&gt;, &lt;code&gt;node .output/server/index.mjs&lt;/code&gt;) that depends on the surrounding directory structure being intact.&lt;/p&gt;

&lt;p&gt;It should be possible to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;some-framework build ./App.jsx &lt;span class="nt"&gt;-o&lt;/span&gt; app.js
node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file in. One file out. No &lt;code&gt;node_modules&lt;/code&gt;, no config, no manifest, no &lt;code&gt;dist/&lt;/code&gt; to preserve. A single &lt;code&gt;.js&lt;/code&gt; that boots an HTTP server, serves the assets it needs (inlined or referenced), and runs on any Node install with nothing else next to it.&lt;/p&gt;

&lt;p&gt;Backend developers have had this for years, just under different names. Go produces a static binary. Deno compiles to a single executable. esbuild can bundle a Node program into one file. The pattern is universal: take everything the program needs, fold it into one artifact, ship that. Nothing about a React app — even a server-rendered, server-component-heavy React app — fundamentally prevents the same thing.&lt;/p&gt;

&lt;p&gt;What this unlocks is bigger than convenience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trivial deployment.&lt;/strong&gt; &lt;code&gt;scp app.js server:/srv/ &amp;amp;&amp;amp; node app.js&lt;/code&gt;. No CI artifact pipelines, no Docker images for a webhook receiver, no Kubernetes for a status page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility.&lt;/strong&gt; The artifact is a file. You can hash it, version it, archive it, email it. Not a directory whose contents quietly differ depending on which &lt;code&gt;npm install&lt;/code&gt; produced it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxes.&lt;/strong&gt; A single file is something a sandbox runtime — a serverless platform, a worker, a container — can swallow whole, with no need to mount a &lt;code&gt;node_modules&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution.&lt;/strong&gt; Internal tools become as easy to share as a CLI binary. "Drop this on the server and run it" is a workflow we lost the moment frontends grew a build directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deploy story for a small app should be as small as the app. Right now, even a thirty-line frontend deploys like a monorepo.&lt;/p&gt;

&lt;h2&gt;
  
  
  And then AI showed up
&lt;/h2&gt;

&lt;p&gt;The scaffold tax used to be paid mostly by humans — a one-time annoyance you absorbed at project start, then forgot about. AI coding tools have quietly turned it into a recurring tax, paid on every interaction.&lt;/p&gt;

&lt;p&gt;When you ask an AI to modify a single-file app, it can read the entire program in one shot, hold the whole behavior in its working memory, and reason about a change with confidence. The file &lt;em&gt;is&lt;/em&gt; the project. There is nothing else to discover.&lt;/p&gt;

&lt;p&gt;When you ask an AI to modify a scaffolded project, it has to do archaeology first. Where does routing live? Which &lt;code&gt;tsconfig&lt;/code&gt; paths are aliased? Is that import resolved by a framework convention or by the bundler? Is &lt;code&gt;app/&lt;/code&gt; the routing root, or a coincidentally-named folder? What does the project's ESLint config forbid? Half the request gets spent loading context that wasn't actually relevant to the change.&lt;/p&gt;

&lt;p&gt;This shows up as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Worse answers&lt;/strong&gt;, because the model is reasoning under a noisier prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slower answers&lt;/strong&gt;, because more files have to be read before it can act.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More expensive answers&lt;/strong&gt;, because tokens are not free, and a fresh agent re-discovers the same project structure on every session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More fragile answers&lt;/strong&gt;, because the model has more surface area on which to misread a convention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A one-file app is, by accident, the ideal substrate for AI-assisted coding: the entire program fits in a single attention window, every symbol resolves locally, and the change you ask for can be made without crawling a directory tree first. The convention overhead we built up to make starting a project "easier" turns out to be overhead we now pay &lt;em&gt;every time&lt;/em&gt; we ask a tool to help us edit one.&lt;/p&gt;

&lt;p&gt;The same things that made the single-file app pleasant to write by hand — small surface, no hidden conventions, nothing to discover — make it the format AI tools handle best. We just stopped producing apps in that shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;There is a subtle compounding effect to all of this. When the cost of starting is high, people start fewer things. When people start fewer things, the ecosystem gets less weird, less experimental, less playful. The thirty-line idea that would have become a beloved internal tool never gets written, because the scaffolding tax was higher than the energy budget for the experiment.&lt;/p&gt;

&lt;p&gt;The modern frontend stack is extraordinarily capable. It can render server components, stream HTML, hydrate selectively, generate static pages, run on the edge, do incremental builds. None of that is at odds with also being able to do &lt;em&gt;this&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;some-framework dev ./App.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a small surface area. It's enormously valuable. And it is, conspicuously, missing from almost every option you'd reach for today.&lt;/p&gt;

&lt;p&gt;The good news is that &lt;em&gt;almost&lt;/em&gt;. If you look around carefully, this capability is starting to reappear in the corners of the ecosystem — runtimes that treat the single file as a first-class entry point, not as a degenerate case of a project. It's worth keeping an eye on.&lt;/p&gt;

&lt;p&gt;The thirty-second app deserves to come back.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Two Joys of Coding Before AI</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Mon, 27 Apr 2026 19:51:28 +0000</pubDate>
      <link>https://dev.to/lazarv/the-two-joys-of-coding-before-ai-1pbp</link>
      <guid>https://dev.to/lazarv/the-two-joys-of-coding-before-ai-1pbp</guid>
      <description>&lt;p&gt;There is a particular kind of grief floating around right now. You see it in blog posts, in conference talks, in late-night threads: a mourning for the joy of coding before AI. People describe it as if a forest has been paved over. Something they loved is gone, and something colder has taken its place.&lt;/p&gt;

&lt;p&gt;I think most of these conversations talk past each other because they skip the only question that matters: &lt;strong&gt;what was the joy actually made of?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Coding" is not one activity. It is at least two, braided together so tightly that for decades nobody had to separate them. AI pulls on one of those strands and not the other, and whether that feels like loss or liberation depends entirely on which strand you were holding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of joy
&lt;/h2&gt;

&lt;p&gt;Strip a programming session down to its emotional core and you find two distinct rewards.&lt;/p&gt;

&lt;p&gt;The first is &lt;strong&gt;the joy of materializing a vision&lt;/strong&gt;. You see something in your head — a tool, an interface, a system, a small clever thing that does not exist yet — and you bring it into the world. The pleasure here is in the gap closing. The thing in your imagination and the thing on the screen converge until they are the same thing. The keyboard, the language, the build system: these are friction. Necessary friction, often beautiful friction, but friction. The joy lives at the moment of arrival.&lt;/p&gt;

&lt;p&gt;The second is &lt;strong&gt;the joy of figuring something out&lt;/strong&gt;. A problem resists you. You sit with it. You pull on threads, build mental models, get them wrong, refine them, and somewhere — sometimes in the shower, sometimes at 2 a.m., sometimes mid-sentence in a meeting — the shape of the answer clicks into place. The pleasure here is not in arrival but in the act of comprehension itself. You understand something now that you did not understand an hour ago, and your brain rewards you for it the way it rewards eating when you are hungry.&lt;/p&gt;

&lt;p&gt;These are not the same feeling. They use different muscles. They satisfy different hungers. And — this is the important part — they leave behind different kinds of memory. A vision-materializer remembers what they built. A problem-solver remembers how the world bent into a new shape inside their head.&lt;/p&gt;

&lt;p&gt;Most working programmers feel both, in different proportions, sometimes in the same hour. But if you ask honestly which one is the &lt;em&gt;core&lt;/em&gt; — the thing that made you a programmer rather than something else — almost everyone has an answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI actually changes
&lt;/h2&gt;

&lt;p&gt;AI coding assistants are extraordinary materializers. That is what they are built to be. You describe the thing, they produce the thing. The friction between vision and artifact collapses dramatically. What used to be an afternoon of plumbing is now a paragraph and a review pass.&lt;/p&gt;

&lt;p&gt;If your joy was in the materialization — in seeing the thing exist — AI is not stealing anything from you. It is &lt;strong&gt;giving you more of what you loved&lt;/strong&gt;. The gap closes faster, which means you can close more gaps, which means more visions per unit of life. The friction you tolerated was never the source of the joy; the arrival was. You can build the second thing now, and the third, and the weird side project you never had time for. The hands-on craft loss is real but it is a craft loss, not a joy loss. You can still write the loop by hand on a Saturday if you want to. Nothing stops you.&lt;/p&gt;

&lt;p&gt;One thing has to be said clearly here, because it is the most common bad-faith reading of the materializer position: &lt;strong&gt;materialization is not "whatever shipped, shipped."&lt;/strong&gt; A vision is not just a silhouette of a feature; it has internal coherence, a way it behaves under pressure, a quality it carries. A materializer who accepts slop because it superficially resembles the artifact in their head has not closed the gap — they have moved the goalposts to meet the output. That is not the joy of materializing a vision. That is the relief of being done. They are different feelings, and conflating them is how teams end up shipping confident-sounding garbage at unprecedented speed. The AI gives you a draft. The work — the actual materializer's work — is to keep pushing the draft until it matches the thing in your head, including the parts of the thing in your head that have to do with correctness, taste, performance, and how it will read to the next person. Acceleration is acceleration toward the &lt;em&gt;right&lt;/em&gt; artifact, not toward any artifact. A materializer who forgets this is no longer practicing their craft; they are just hitting accept.&lt;/p&gt;

&lt;p&gt;If your joy was in the figuring out, the picture is genuinely different — and the grief is genuinely earned. The AI is not just removing friction; it is removing &lt;strong&gt;the problem itself&lt;/strong&gt;. The puzzle you would have sat with for three days, turning it over in your head on the train and in the shower, building the mental model that becomes part of how you think forever — the AI hands you an answer in twelve seconds. The answer is often correct. And the comprehension that would have grown in you while you struggled does not grow, because you did not struggle.&lt;/p&gt;

&lt;p&gt;This is not a complaint about laziness or skill atrophy, though those are real concerns. It is something more specific: a category of human pleasure, the pleasure of &lt;em&gt;understanding something hard&lt;/em&gt;, requires the hardness. Remove the hardness and you remove the pleasure, even if you keep the answer. You cannot have the satisfaction of a crossword without the crossword.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the debate is so confused
&lt;/h2&gt;

&lt;p&gt;Once you see this split, the public conversation about AI and coding starts to make more sense. The two camps are not actually disagreeing about AI. They are reporting honestly on two different inner experiences.&lt;/p&gt;

&lt;p&gt;The "AI is wonderful, I ship five times faster" camp is overwhelmingly populated by materializers. They are telling the truth. Their joy is intact and amplified.&lt;/p&gt;

&lt;p&gt;The "AI is hollowing out my craft" camp is overwhelmingly populated by problem-solvers. They are also telling the truth. Their joy is, in fact, being eroded — not by malice or hype, but by the specific mechanism of having the puzzles solved before they get to play with them.&lt;/p&gt;

&lt;p&gt;When these two groups argue, they sound like they are arguing about a tool. They are actually arguing about which of two pleasures is the real one. There is no answer to that question, because there are two answers, and they are both correct for the person giving them.&lt;/p&gt;

&lt;p&gt;Notice how each camp uses the same phrase to dismiss the other's grief. To the materializer, the problem-solving was always an &lt;em&gt;implementation detail&lt;/em&gt; — a means, a tax you paid on the way to the artifact, something a sufficiently advanced tool was supposed to absorb eventually. To the problem-solver, the shipped artifact was the implementation detail — the residue, the visible echo of an internal event that had already happened in their head. Each side, in good faith, treats the other side's joy as the boring scaffolding around their own. That is why the conversation goes nowhere: both sides are correctly identifying what is, &lt;em&gt;for them&lt;/em&gt;, incidental.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do about it
&lt;/h2&gt;

&lt;p&gt;If you are mourning, the first useful move is to ask yourself the honest version of the question. Not "do I miss coding," but: &lt;em&gt;which kind of coding?&lt;/em&gt; When you replay the moments you would call joyful, are you watching yourself ship the thing, or are you watching yourself understand the thing? The answer tells you whether you are facing an opportunity or a loss.&lt;/p&gt;

&lt;p&gt;For materializers, the path is mostly forward. Use the tools. Build more. The thing you loved is more available now, not less.&lt;/p&gt;

&lt;p&gt;For problem-solvers, the answer is harder and more deliberate. The puzzles still exist; they have just stopped arriving on their own. Production code paths now route around them. To keep the joy, you have to &lt;strong&gt;choose the friction back in&lt;/strong&gt; — pick problems the AI cannot solve cleanly, work in domains where the model is weak, build from scratch on weekends, read papers, do the leetcode-equivalent that is actually interesting to you, contribute to runtimes and compilers and other places where the problem space is still deep enough that no autocomplete can shortcut it. The protected hour where you do not ask the assistant is not a Luddite stance; it is a deliberate preservation of the conditions your joy requires.&lt;/p&gt;

&lt;p&gt;Both responses are healthy. Both are grown-up. What is not healthy is conflating them — using a materializer's optimism to dismiss a problem-solver's grief, or using a problem-solver's grief to deny a materializer's genuine, earned acceleration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing under the thing
&lt;/h2&gt;

&lt;p&gt;The deeper claim hiding inside all of this is that &lt;em&gt;coding was never one thing&lt;/em&gt;. It was a workbench where two very different human pleasures happened to use the same tools. The industry treated them as one because the workflow forced them to be one — you could not materialize a vision without solving a hundred small problems along the way, and you could not solve interesting problems without something to materialize them into.&lt;/p&gt;

&lt;p&gt;AI is the first force strong enough to pull those two pleasures apart. It is doing so cleanly and without asking permission. What we are watching is not the death of the joy of coding. It is the unbundling of two joys that were always separate, finally being forced to admit it.&lt;/p&gt;

&lt;p&gt;Which one was yours?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>RSC as a serializer, not a model</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Mon, 27 Apr 2026 17:32:02 +0000</pubDate>
      <link>https://dev.to/lazarv/rsc-as-a-serializer-not-a-model-56nj</link>
      <guid>https://dev.to/lazarv/rsc-as-a-serializer-not-a-model-56nj</guid>
      <description>&lt;p&gt;The most interesting thing about how TanStack Start integrates React Server Components is not what it does with them. It is the shape of the API.&lt;/p&gt;

&lt;p&gt;You do not write a Server Component. You do not opt into a model. You opt into a &lt;em&gt;function&lt;/em&gt; — a programmatic primitive that takes a component, runs it through the RSC renderer, and hands you back a payload you can store, transport, and rehydrate later. RSC is not the substrate of the application. It is a tool you reach for at specific call sites, when you want a specific thing.&lt;/p&gt;

&lt;p&gt;That framing is worth pausing on, because it tells you something about the design intent that the marketing copy does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the API is really for
&lt;/h2&gt;

&lt;p&gt;The use case being served is narrow and clear: caching values that the rest of the JavaScript ecosystem cannot cache. A normal cache stores JSON. JSON cannot represent a React element. JSON cannot pass through a value that resolves later. JSON cannot carry the richer plain types — Map, Set, Date, typed arrays — that show up in a real component tree. RSC's wire format can. It was &lt;em&gt;designed&lt;/em&gt; to. That is what the protocol is.&lt;/p&gt;

&lt;p&gt;So if you take the RSC renderer, strip it of everything that makes it a composition model, and expose just the serializer, you get a primitive that turns "a tree containing UI and non-JSON props" into "a string you can put in Redis." That is a real capability. It is also, on reflection, a very small one.&lt;/p&gt;

&lt;p&gt;This is the capability TanStack Start has surfaced. Not Server Components. A serializer for component-shaped values, exposed as a programmatic API.&lt;/p&gt;

&lt;h2&gt;
  
  
  If the goal is caching, name it caching
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;"use cache"&lt;/code&gt; is the design that already exists for this exact problem. It is a directive. It tells the runtime: &lt;em&gt;this output is cacheable, key it on these inputs, store it for this long&lt;/em&gt;. The runtime handles serialization, deserialization, key derivation, invalidation, and storage. The developer writes a function and adds one line.&lt;/p&gt;

&lt;p&gt;Concretely. Imagine a post card — a tree containing a server-rendered article and a client-rendered set of actions — produced once and reused across navigations. Under a directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PostCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;postId&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&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;db&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="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="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="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;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;&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;body&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="nc"&gt;PostActions&lt;/span&gt; &lt;span class="na"&gt;postId&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="na"&gt;authorId&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;authorId&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;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime knows what the cache key is (the inputs the function closes over), where the value lives, when it expires, and how to serialize it (the same wire format the rest of the system already speaks). One line of metadata and a function body. The developer writes a component.&lt;/p&gt;

&lt;p&gt;Under TanStack Start's API, the same tree is produced by driving the protocol explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;createServerFn&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;@tanstack/react-start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCompositeComponent&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;@tanstack/react-start/rsc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPostCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerFn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&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;data&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;post&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;db&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="nf"&gt;findById&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="nx"&gt;postId&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;src&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;createCompositeComponent&lt;/span&gt;&lt;span class="p"&gt;(&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;renderActions&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;postId&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;authorId&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="o"&gt;=&amp;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;ReactNode&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;article&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="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;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;&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;body&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;footer&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderActions&lt;/span&gt;&lt;span class="p"&gt;?.({&lt;/span&gt; &lt;span class="na"&gt;postId&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;authorId&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="nx"&gt;authorId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;footer&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;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&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;Route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFileRoute&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/$postId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&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;getPostCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PostPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useLoaderData&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;CompositeComponent&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;renderActions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authorId&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="nc"&gt;PostActions&lt;/span&gt; &lt;span class="na"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;authorId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authorId&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six things have to agree across this code: the server function that produces the payload, the validator that gates its inputs, the &lt;code&gt;createCompositeComponent&lt;/code&gt; call that names the seam, the loader entry on the route, the &lt;code&gt;useLoaderData&lt;/code&gt; site that retrieves the payload, and the &lt;code&gt;&amp;lt;CompositeComponent&amp;gt;&lt;/code&gt; site that rehydrates it — including the render-prop signature that has to match on both sides. Caching is implicit in the router's &lt;code&gt;staleTime&lt;/code&gt; and &lt;code&gt;loaderDeps&lt;/code&gt;. Invalidation is the router's responsibility and shaped by the route boundary, not the value boundary.&lt;/p&gt;

&lt;p&gt;None of these decisions have a defensible non-canonical answer for the simple case. Every team using this will write the same wrapper around it, in slightly different shapes, and the bugs will all live in the relationships between the serialization site, the render-prop signature, and the rehydration site.&lt;/p&gt;

&lt;p&gt;The case where this matters most is the one the framework does not address at all: caching a component-shaped value at request scope. The docs recommend &lt;code&gt;React.cache&lt;/code&gt; for the request-scoped case, but &lt;code&gt;React.cache&lt;/code&gt; deduplicates plain function results — it does not memoize trees that contain client component references and closures over server-only data. There is no primitive shaped for that. The same computation can run three times in one request because three parts of the tree need it, and the framework's answer is the route-level cache, which is route-scoped, or &lt;code&gt;React.cache&lt;/code&gt;, which does not apply.&lt;/p&gt;

&lt;p&gt;A directive-shaped API hides those decisions because most of them only have one correct answer. A library-shaped API exposes them because the framework refuses to commit to one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boundary wrappers, protocol wrappers
&lt;/h2&gt;

&lt;p&gt;There is a more general problem under this. RSC, as a &lt;em&gt;protocol&lt;/em&gt;, is a serialization format. RSC, as a &lt;em&gt;model&lt;/em&gt;, is a composition story between two environments. When a framework lifts the protocol out of the model and exposes it as a programmable primitive, what it is really saying is: "the protocol is interesting to us, the model is not."&lt;/p&gt;

&lt;p&gt;It would be easy to read this as a complaint about wrappers in general. It is not. A wrapper-shaped API is often the right answer, and in TanStack Start specifically it is the dominant idiom. Routes are wrappers. Loaders are wrappers. Server functions are wrappers. The whole framework is built around the pattern of &lt;em&gt;pass your thing through this constructor, get back a richer thing the runtime now understands&lt;/em&gt;. A wrapper for RSC caching slots into that idiom cleanly.&lt;/p&gt;

&lt;p&gt;The argument is about what a wrapper &lt;em&gt;carries&lt;/em&gt; across the boundary it creates, because there are two kinds and they look almost identical until you start using them.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;boundary&lt;/em&gt; wrapper says: this is where the model changes shape. It takes a value, marks a seam the runtime will operate on, and hands the developer a richer reference back. The developer never touches the protocol underneath; they only see the seam, named and located. The wrapper carries the model with it — calling it is participating in the model.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;protocol&lt;/em&gt; wrapper says: here is the renderer, here is the payload, here is the deserializer. It hands the developer the wire format and asks them to drive it. The seam is gone. What is left is a set of operations on bytes that the developer has to compose themselves.&lt;/p&gt;

&lt;p&gt;TanStack Start's other wrappers — the route, the loader, the server function — are boundary wrappers. The framework knows what the wrapped thing is, the runtime knows what to do with it, and the developer writes their code in the vocabulary of the model rather than the vocabulary of the protocol underneath. That is what makes those APIs feel coherent next to each other.&lt;/p&gt;

&lt;p&gt;The RSC integration is the exception. The outer shape is the same — a function you call, a value you hand it, a value you get back — but there is no model on the other side. The developer is handed the renderer instead of being handed a richer reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The defensible version of the choice
&lt;/h2&gt;

&lt;p&gt;The strongest case for what TanStack has shipped goes something like this. The framework's audience is router-driven and SPA-adjacent. Committing to the full RSC composition model would constrain the rest of the architecture in ways that conflict with how the framework already works. A library-shaped API leaves room for teams whose caching needs really do diverge. A directive forces a single shape on every caller, and that shape — &lt;code&gt;"use cache"&lt;/code&gt; as it ships today — is itself not a settled design. Its key inference, its invalidation story, and its "what is safe to close over" semantics have known warts. Why commit to it?&lt;/p&gt;

&lt;p&gt;The first half of that case is real. The second half is the one to push back on.&lt;/p&gt;

&lt;p&gt;A directive does not have to mean &lt;code&gt;"use cache"&lt;/code&gt; exactly as it ships today. The directive is a shape, not a specification. The shape is: a marker at the boundary, a runtime that infers the keying and storage, a developer who writes a function. The runtime gets to choose how aggressive the inference is, what scope the directive defaults to, and what the escape hatches look like. A request-scoped variant is &lt;em&gt;easier&lt;/em&gt; than the application-cache variant &lt;code&gt;"use cache"&lt;/code&gt; currently targets, not harder, because almost every decision has one canonical answer at the request scope.&lt;/p&gt;

&lt;p&gt;The "we left it open because needs diverge" defense applies to primitives where they really do diverge. Application caches diverge — TTLs, tags, storage. Request-scoped caches do not. The needs are: dedupe within a render, survive into hydration, evaporate at request end. That is the specification. There is no second team whose answer to those questions is meaningfully different.&lt;/p&gt;

&lt;p&gt;The honest version of the architectural choice is: "we don't want to commit to the model yet, and we don't want to ship a caching directive without committing to the model, so we shipped the renderer as a library and let users assemble what they need." That is a coherent position. It is just not the position the docs reflect.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually being shipped
&lt;/h2&gt;

&lt;p&gt;To make this caching primitive work, the framework has to bundle the RSC renderer, the RSC serializer, the matching deserializer, the streaming format reader, and enough of the React internals to drive all of it. That is the entire RSC machinery, intact — including the parts the framework will not let the developer use.&lt;/p&gt;

&lt;p&gt;The framework does not have a &lt;code&gt;"use client"&lt;/code&gt; boundary at all. Interactivity is injected through slots on &lt;code&gt;&amp;lt;CompositeComponent&amp;gt;&lt;/code&gt; — &lt;code&gt;children&lt;/code&gt;, render props, component props — not through client component references resolved at hydration. The reference-resolution machinery RSC was designed around, the matched identifiers across server and client builds, the client manifest, the hydration-time dispatch, all of it is bundled but inert. The framework has actively engineered an alternative path (the slot pattern) that routes around the very mechanism RSC exists to provide. The user experience of writing a TanStack Start application is the user experience of writing a router-driven app that occasionally summons the RSC serializer for caching.&lt;/p&gt;

&lt;p&gt;So the framework pays the full cost of the protocol — every byte of the serializer, every transitive dependency, every line of React internals it has to remain compatible with — and uses it to power a feature that, on its own, could be expressed as a directive over a much smaller mechanism. The bundle includes the engine of a model the framework has chosen not to adopt.&lt;/p&gt;

&lt;p&gt;There is a third option the design did not take: a fit-for-purpose serializer. The capability TanStack Start actually needs — turning a rendered component tree, slot placeholders and non-JSON values included, into bytes and back — is a small one. It is not the RSC protocol. RSC is that capability &lt;em&gt;plus&lt;/em&gt; a streaming format with suspense boundaries, &lt;em&gt;plus&lt;/em&gt; server reference dispatch, &lt;em&gt;plus&lt;/em&gt; the entire client-resolution layer the framework has already chosen not to use. A protocol designed for the narrower job would be smaller, evolvable on the framework's own cadence, and decoupled from React's internals. That a project willing to ship its own router, its own loader system, its own server-function wrapper, and its own composite-component primitive nevertheless reached for the entire RSC implementation tells you the choice was not driven by what the feature needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent reading the code
&lt;/h2&gt;

&lt;p&gt;There is one more lens worth applying, because in 2026 it is the lens that shapes a growing share of code: the AI coding agent.&lt;/p&gt;

&lt;p&gt;The relevant claim is narrower than "agents will get directives right and wrappers wrong." It is structural: a directive compresses the model into the syntax; a protocol wrapper requires the model to be reconstructed at every call site, and that reconstruction is where coordination bugs compound.&lt;/p&gt;

&lt;p&gt;An agent producing a directive-based caching feature emits a marker, a function body, and the inputs the developer already named. The keying, scope, invalidation, and lifetime are the runtime's contract — the agent does not produce them, so it cannot produce them wrong.&lt;/p&gt;

&lt;p&gt;An agent producing a protocol-wrapper caching feature has to emit a serialization call, a storage decision, a key derivation, an invalidation hook, a deserialization call, a rehydration site, and the glue holding all of them in agreement. The shape of each piece is locally reasonable. The bug lives in the relationships — a stale key, a missed invalidation hook, a request-scoped value silently captured in the payload, a payload rehydrated at the wrong boundary. An agent reviewing its own output will not flag these, because each part on its own makes sense.&lt;/p&gt;

&lt;p&gt;Less code is not only easier for humans. It is easier for agents — fewer tokens to generate, fewer relationships to track, fewer places to be subtly wrong. Boundary primitives compress an enormous amount of model into a tiny amount of syntax. Protocol APIs decompress the model back into surface area and ask the agent to operate it. Increasingly, an API surface that is hostile to agents is an API surface that is hostile to its own users.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets lost
&lt;/h2&gt;

&lt;p&gt;A framework gets to choose the level of abstraction it commits to. If it commits to RSC as a model, the developer writes components and the runtime handles the seams. If it commits to caching as a model, the developer writes directives and the runtime handles serialization. If it commits to neither and exposes the protocol as a library, the developer writes glue.&lt;/p&gt;

&lt;p&gt;Glue is the most expensive code in any application. It is the code that does not solve the problem; it only connects the things that do. It also ages worst, because every change to the surrounding ecosystem requires the glue to be rewritten while the parts on either side of it stay the same. The moment a request-scoped value becomes something the developer has to manually serialize, key, store, and revive, the framework has already lost the lifecycle it was supposed to protect.&lt;/p&gt;

&lt;p&gt;A framework whose RSC story is a library call is asking every team that uses it to write and maintain that glue forever. The convenience is moved out of the runtime and into a thousand small repositories that all rediscover the same patterns and the same failure modes. The framework gets to ship a thinner runtime; the ecosystem absorbs the cost.&lt;/p&gt;

&lt;p&gt;That trade is sometimes worth it — for a primitive that genuinely has no canonical shape. Caching component output is not that primitive. It has a canonical shape. The shape is a directive. The reason it is not the API is not that the shape is wrong. It is that adopting the shape would require committing to the surrounding model, and that commitment was the thing the framework was avoiding from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The smaller point
&lt;/h2&gt;

&lt;p&gt;Strip the protocol away and the design becomes legible. The team wanted a way to cache values that JSON could not represent. They had access to a serializer that could. They exposed the serializer. They called it RSC support.&lt;/p&gt;

&lt;p&gt;It is RSC support in the sense that the renderer is in the bundle. It is not RSC support in the sense that the developer is asked to think in the RSC model, write in its idioms, or benefit from its composition story. The renderer is a load-bearing dependency for a feature that is not, itself, the model the renderer was designed to power.&lt;/p&gt;

&lt;p&gt;When a framework treats a model as a primitive instead of as the model, it is telling you that it wanted something the model happened to make possible, not the model itself. There is nothing wrong with that as a product decision — but it should be named accurately. The honest version of this feature is a caching directive. The honest version of the framing is "we ship a serializer-backed cache." Everything else is a story told in the vocabulary of a model the framework chose not to adopt.&lt;/p&gt;

</description>
      <category>rsc</category>
      <category>tanstack</category>
      <category>react</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Project, Don't Embed: Introducing Virtual Frame</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Tue, 21 Apr 2026 08:48:43 +0000</pubDate>
      <link>https://dev.to/lazarv/project-dont-embed-introducing-virtual-frame-klb</link>
      <guid>https://dev.to/lazarv/project-dont-embed-introducing-virtual-frame-klb</guid>
      <description>&lt;p&gt;&lt;em&gt;A first-time introduction to Virtual Frame — what it is, why it exists, and how it composes independently deployed web apps into a single page without a shared build.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's a problem every large frontend eventually runs into, and it goes something like this: you have five teams, five stacks, five deployment pipelines — and one page. The design system lives in team A's repo. The checkout widget belongs to team B. The dashboard you're trying to embed was last touched by a team that no longer exists. None of these things ship together. All of them need to render on the same page, at the same time, and feel like one product.&lt;/p&gt;

&lt;p&gt;The industry has been trying to solve this for a decade. The solutions all have a tax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module federation&lt;/strong&gt; is great when host and remote share a build pipeline, and useless the moment they don't. The first time you need a coordinated upgrade across three repos, the "independent teams" story evaporates. &lt;strong&gt;Iframes&lt;/strong&gt; give you perfect isolation and zero composability — you get a rigid rectangle that doesn't flow with your layout, can't inherit your theme, and picks fights about scroll, focus, and accessibility. &lt;strong&gt;Edge-side includes and server fragments&lt;/strong&gt; work beautifully for static markup and fall apart the moment the remote needs its own runtime. &lt;strong&gt;Ad-hoc SPA shells&lt;/strong&gt; work until they don't; then you're debugging a shared React instance that sees two different versions of the same context.&lt;/p&gt;

&lt;p&gt;Each of these is the right answer to a different question. None of them is the right answer to the question most teams are actually asking: &lt;em&gt;how do I compose fully independent web applications — different repos, different frameworks, different deploys — into one page, with real layout flow and real interactivity, without coupling any of them at build time?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Virtual Frame is a bet that you can do that if you stop thinking about &lt;em&gt;embedding&lt;/em&gt; remote applications and start thinking about &lt;em&gt;projecting&lt;/em&gt; them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-paragraph version
&lt;/h2&gt;

&lt;p&gt;Picture a hidden iframe loading another team's page. The page runs normally in there — same as if you'd opened it in a new tab. Virtual Frame takes what that hidden page is drawing and paints it into a slot on your own page, live. When the remote updates, your slot updates. When your users click, scroll, or type inside the slot, Virtual Frame forwards those interactions back to the hidden page so its app keeps working like it's running in a real browser tab.&lt;/p&gt;

&lt;p&gt;The result: the remote app's output is part of your page. It flows with your layout, picks up your theme, and behaves like any other piece of your UI — because, as far as the browser is concerned, it &lt;em&gt;is&lt;/em&gt; any other piece of your UI. The remote keeps running in its own world; its output lives in yours.&lt;/p&gt;

&lt;p&gt;That's the entire pitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this mental shift matters
&lt;/h2&gt;

&lt;p&gt;The most important word in the previous section is &lt;em&gt;projecting&lt;/em&gt;, and it's worth slowing down on.&lt;/p&gt;

&lt;p&gt;When you embed something — an iframe, a web component, a third-party widget — you're accepting its frame. The embedded thing has a boundary, and things stop at that boundary. Layout stops there. Events stop there. Themes stop there. You're not composing, you're docking.&lt;/p&gt;

&lt;p&gt;When you &lt;em&gt;project&lt;/em&gt;, the boundary collapses. The remote application still runs in its own browsing context — that part is non-negotiable, it's how you keep the remote's runtime from tripping over yours — but its rendered output flows into your page the way a child component's output would. The &lt;em&gt;execution&lt;/em&gt; is isolated; the &lt;em&gt;presentation&lt;/em&gt; is composed.&lt;/p&gt;

&lt;p&gt;This is a surprisingly slippery idea the first time you encounter it, because we've been trained by twenty years of iframe behavior to think of "other-origin content" and "rigid rectangle" as the same thing. They're not. The rigidity is an implementation detail of how browsers expose iframe contents, not a law of nature.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it actually works
&lt;/h2&gt;

&lt;p&gt;Three primitives, no magic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A source iframe&lt;/strong&gt;, hidden off-screen. The remote runs as a complete, standalone application inside it — its framework boots, its router runs, its effects fire, its fonts load. Virtual Frame doesn't re-execute your app; it observes it. That's the key constraint: nothing about your remote has to change to be projectable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A host element&lt;/strong&gt;, which is any element you put on the page — a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, a &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, a component root. Virtual Frame optionally attaches a Shadow DOM to it (open or closed) and mirrors the remote's &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; subtree into that shadow root. Shadow DOM gives you CSS isolation without giving up custom-property inheritance, which is the sweet spot: the remote's styles can't bleed into your page, but your theme still crosses the boundary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A sync layer&lt;/strong&gt;. Same-origin, this is a MutationObserver on the source plus CSS rewriting and event re-dispatch. Cross-origin, a bridge script loaded once in the remote serializes the DOM and events over &lt;code&gt;postMessage&lt;/code&gt;, and the host reconstructs them. The cross-origin story is worth dwelling on, because this is where iframes usually give up: &lt;em&gt;there is no host-side configuration&lt;/em&gt;. Drop the bridge into the remote, and every host on every origin can project it.&lt;/p&gt;

&lt;p&gt;Everything else — selector-based projection, canvas streaming, SSR with resumption, the shared reactive store — is built on top of these three primitives. But the primitives are simple, and that's on purpose. Simple primitives compose.&lt;/p&gt;

&lt;h2&gt;
  
  
  A taste of code
&lt;/h2&gt;

&lt;p&gt;The shortest path is the custom element. This is a complete working projection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;virtual-frame/element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;virtual-frame&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://dashboard.example.com"&lt;/span&gt;
  &lt;span class="na"&gt;isolate=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width: 100%; height: 600px"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/virtual-frame&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No build plugin. No config file. No framework buy-in. When the element connects to the DOM, it creates a hidden iframe at &lt;code&gt;src&lt;/code&gt;, attaches a shadow root to the custom element, and starts projecting. When the element is removed, everything tears down.&lt;/p&gt;

&lt;p&gt;If you want to project only a piece of the remote — a chart, a panel, a sidebar — add a selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;virtual-frame&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://dashboard.example.com"&lt;/span&gt;
  &lt;span class="na"&gt;selector=&lt;/span&gt;&lt;span class="s"&gt;"#metrics-chart"&lt;/span&gt;
  &lt;span class="na"&gt;isolate=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/virtual-frame&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full remote still runs in the background, so the selected subtree behaves exactly as it would in its native page. Its event handlers work. Its data fetches work. Its animations work. You just pulled a widget out of another team's app without negotiating an API contract.&lt;/p&gt;

&lt;p&gt;If you're in React, it's a component:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VirtualFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useVirtualFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useStore&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;@virtual-frame/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createStore&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;@virtual-frame/store&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;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Dashboard&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;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&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;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVirtualFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://remote.example.com&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="nx"&gt;store&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;&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;Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&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="nc"&gt;VirtualFrame&lt;/span&gt; &lt;span class="na"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#counter"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;The &lt;code&gt;store&lt;/code&gt; there is worth a second look. It's an event-sourced, proxy-based reactive object that synchronizes between host and remote automatically. Read and write it like a plain object on either side; mutations propagate over the same message channel the projection uses. It's the piece you reach for when "host and remote need to share state" comes up, and it means you don't have to invent a coordination protocol.&lt;/p&gt;

&lt;p&gt;There are first-class bindings for Vue, Svelte, Solid, Angular, Next.js, Nuxt, SvelteKit, TanStack Start, SolidStart, Analog, React Router, and a few more. They all sit on the same core engine. Pick whichever is closest to how your page is built.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you actually get
&lt;/h2&gt;

&lt;p&gt;A few consequences of the projection model that are worth naming explicitly, because they're the things that tend to surprise people:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout flow just works.&lt;/strong&gt; Projected content fills its host element's box and participates in flex and grid like any other child. No &lt;code&gt;width="100%"&lt;/code&gt;-then-cry dance. No resize observer hacks. No scroll-inside-scroll confusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theming crosses the boundary.&lt;/strong&gt; CSS custom properties inherit into the shadow root, so your design tokens — colors, spacing, fonts, dark mode — reach the projection without any coordination with the remote team. They don't even need to know your tokens exist; they just need to use &lt;code&gt;var()&lt;/code&gt; for things they want themeable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility is inherited.&lt;/strong&gt; The projected DOM is real DOM in your tree, so focus traversal, screen readers, and keyboard navigation see it as part of the page. You're not fighting the iframe a11y model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSR with resumption.&lt;/strong&gt; The meta-framework integrations can server-fetch the remote, inline the projection inside declarative Shadow DOM, and resume on the client without a second round-trip. First paint is styled and interactive content arrives without the iframe flash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-origin without proxy gymnastics.&lt;/strong&gt; No CORS negotiations. No server-side proxy. Ship the bridge once in the remote, and every host everywhere can project it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it isn't
&lt;/h2&gt;

&lt;p&gt;Worth being honest about the non-goals, because they come up in design reviews and getting them wrong wastes everyone's time.&lt;/p&gt;

&lt;p&gt;Virtual Frame is &lt;strong&gt;not an iframe replacement&lt;/strong&gt; — it still uses one under the hood, because that's how you give the remote its own browsing context. What's different is that you don't &lt;em&gt;see&lt;/em&gt; the iframe; its DOM is projected into your host.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;not a trust boundary from the host's side&lt;/strong&gt;. The iframe sandboxes the remote's script execution (same-origin policy still applies; the remote's JS can't touch host DOM or globals), but once you project the remote's DOM into your page, your code can read and manipulate that projected tree. If the remote is untrusted, keep the iframe visible and don't project.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;not module federation&lt;/strong&gt;. Nothing is shared at build time. No shared React instance, no shared bundle graph. If you need runtime coordination, use the shared store — a typed message channel, not a hidden dependency on a shared import.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;not a hydration framework&lt;/strong&gt;. The remote hydrates normally inside its own iframe. You don't rewrite your remote app to make it projectable; any web page will do.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to reach for it
&lt;/h2&gt;

&lt;p&gt;Virtual Frame fits well when you need to compose multiple independently deployed apps into one page without coordinating a build. When you want to embed UI from another team, tenant, or origin while keeping layout flow and interactivity native. When you want to retire a user-visible iframe that looks and feels like one. When you want to project only a slice of a remote app and let the rest keep running in the background.&lt;/p&gt;

&lt;p&gt;Skip it when host and remote already share a build — module federation or a plain component export is lighter. Skip it when you need a hard security boundary against an untrusted remote — keep the iframe visible. Skip it when the remote doesn't need interactivity and doesn't change — a server-rendered fragment is simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;The shortest possible next step: &lt;code&gt;npm install virtual-frame&lt;/code&gt;, drop in the custom-element snippet above with a URL you own, and watch it work. Most of the mental model survives contact with five minutes of playing with it.&lt;/p&gt;

&lt;p&gt;When you're ready for more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://virtual-frame.level0x40.com/guide/what-is-virtual-frame" rel="noopener noreferrer"&gt;What is Virtual Frame?&lt;/a&gt;&lt;/strong&gt; — the conceptual overview in depth, including the parts of the mental model this article skipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://virtual-frame.level0x40.com/guide/getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/strong&gt; — installation, the three integration paths, and the first-projection checklist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://virtual-frame.level0x40.com/guide/getting-started#framework-components" rel="noopener noreferrer"&gt;Framework guides&lt;/a&gt;&lt;/strong&gt; — React, Vue, Svelte, Solid, Angular, Next.js, Nuxt, and the rest. Pick yours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://virtual-frame.level0x40.com/guide/cross-origin" rel="noopener noreferrer"&gt;Cross-Origin&lt;/a&gt;&lt;/strong&gt; — the bridge protocol, CSP requirements, and the &lt;code&gt;proxy&lt;/code&gt; option for keeping first-party cookies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://virtual-frame.level0x40.com/guide/store" rel="noopener noreferrer"&gt;Store&lt;/a&gt;&lt;/strong&gt; — the reactive message channel for bidirectional state between host and remote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://virtual-frame.level0x40.com/api/" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;&lt;/strong&gt; — every option, property, and method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thesis, one more time: stop embedding remote applications. Project them. The boundary you've been working around doesn't have to be there.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>frontend</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Illusion of Language: What Directives Really Are</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Sun, 09 Nov 2025 11:35:27 +0000</pubDate>
      <link>https://dev.to/lazarv/the-illusion-of-language-what-directives-really-are-445</link>
      <guid>https://dev.to/lazarv/the-illusion-of-language-what-directives-really-are-445</guid>
      <description>&lt;h2&gt;
  
  
  Preface &amp;amp; Introduction — Why This Post Exists
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post is not a rebuttal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s a reflection inspired by the great &lt;strong&gt;“Directives and the Platform Boundary”&lt;/strong&gt; article by Tanner Linsley. I genuinely enjoyed the piece and agree with many of its points — especially around ownership, provenance, and the value of explicit APIs.&lt;br&gt;
What I wanted to add is a slightly different lens: one that comes from building directive-driven tooling and from having lived through a very similar chapter in programming history, long before JavaScript had &lt;code&gt;'use client'&lt;/code&gt; or &lt;code&gt;'use server'&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over the past year, I’ve watched countless developers treat directives in JavaScript as if they were &lt;strong&gt;actual language features&lt;/strong&gt; — something built into the JavaScript spec or the runtime environment. And to be fair, at a glance, it &lt;em&gt;does&lt;/em&gt; look that way. A string at the top of a file that magically changes how the code behaves feels authoritative.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven’t been following the evolution of React Server Components and frameworks like Next.js: modern React apps now use file-level directives such as &lt;code&gt;'use client'&lt;/code&gt; and &lt;code&gt;'use server'&lt;/code&gt; to control where code runs. They look like simple string literals at the top of a file, but they decide whether a component executes on the server or the client — shaping bundling, rendering and data-flow. That’s where the confusion begins.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But here’s the tension:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directives look like language features.&lt;br&gt;
Directives feel like language features.&lt;br&gt;
Yet directives &lt;em&gt;are&lt;/em&gt; not language features.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They are &lt;strong&gt;tooling-level signals&lt;/strong&gt; — consumed by bundlers, compilers, and build pipelines.&lt;/p&gt;

&lt;p&gt;This misunderstanding is not a new phenomenon.&lt;br&gt;
We’ve been here before.&lt;/p&gt;
&lt;h3&gt;
  
  
  A Personal Flashback
&lt;/h3&gt;

&lt;p&gt;The first time I saw a &lt;code&gt;#pragma once&lt;/code&gt; in a C++ codebase as a young developer, I thought:&lt;br&gt;
“Wow, the language has a keyword for this! Why doesn’t JavaScript?”&lt;/p&gt;

&lt;p&gt;Except… it wasn’t a language keyword.&lt;br&gt;
It was a compiler instruction. A &lt;em&gt;directive&lt;/em&gt;. A hint to the &lt;strong&gt;preprocessor&lt;/strong&gt;, not to the language itself.&lt;/p&gt;

&lt;p&gt;Just like &lt;code&gt;'use strict'&lt;/code&gt; wasn’t “JavaScript syntax” at first — and just like &lt;code&gt;'use client'&lt;/code&gt; isn’t “JavaScript syntax” today.&lt;/p&gt;

&lt;p&gt;This post explores that parallel. Because understanding the &lt;strong&gt;C/C++ preprocessor&lt;/strong&gt; era is a surprisingly powerful way to understand modern JS directives — why they confuse people, and what we could learn from the past to teach them better.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Do Directives Feel Like Language Features?
&lt;/h2&gt;

&lt;p&gt;If you show a newcomer the following code:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Click&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and ask them what &lt;code&gt;'use client'&lt;/code&gt; is, most will confidently answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It’s part of JavaScript.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This isn’t ignorance. It’s &lt;strong&gt;pattern recognition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Throughout their entire programming life, file-level “magic statements” have almost always been &lt;strong&gt;language semantics&lt;/strong&gt;, not tooling semantics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"use strict"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"use asm"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They look like syntax.&lt;br&gt;
They live at the top of the file.&lt;br&gt;
They change behavior.&lt;/p&gt;

&lt;p&gt;So the brain does the obvious thing: it classifies them as language.&lt;/p&gt;

&lt;p&gt;The TanStack article captures part of this well when it says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A directive at the top of a file looks authoritative. It gives the impression of being a language-level truth, not a framework hint.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the crux of the confusion.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Hidden Mechanism Makes It Worse
&lt;/h3&gt;

&lt;p&gt;Directives blur boundaries because the code that reacts to them is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;invisible&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;non-local&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;not traceable through imports&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I write:&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;client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;somewhere&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ I can follow the import. I see who owns it. I can version it. I know what documentation to search for.&lt;/p&gt;

&lt;p&gt;But with directives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there is &lt;strong&gt;no import&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;no namespace&lt;/li&gt;
&lt;li&gt;no ownership reference&lt;/li&gt;
&lt;li&gt;no callsite to inspect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The behavior “comes from nowhere”.&lt;/p&gt;

&lt;p&gt;Which is exactly why they &lt;em&gt;feel&lt;/em&gt; like language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Build Tools… and the Illusion Solidifies
&lt;/h3&gt;

&lt;p&gt;Unlike JavaScript keywords, directives do nothing by themselves.&lt;/p&gt;

&lt;p&gt;A runtime engine won’t throw if you mistype &lt;code&gt;'use clinet'&lt;/code&gt;.&lt;br&gt;
A bundler or transform plugin decides what to do with it. Some ignore it, some error, some treat it as a string literal.&lt;/p&gt;

&lt;p&gt;This leads to the second major confusion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If it requires a bundler to work, isn’t it part of the ecosystem platform, not the language?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the developer’s perspective, &lt;strong&gt;if code changes behavior without explicit imports&lt;/strong&gt;, the mind defaults to:&lt;/p&gt;

&lt;p&gt;✅ language feature&lt;br&gt;
❌ library feature&lt;/p&gt;

&lt;p&gt;And that is the trap.&lt;/p&gt;

&lt;p&gt;Because what we call “platform” today is actually a &lt;strong&gt;stack of tools&lt;/strong&gt; — not a coherent language surface. Exactly like in the C/C++ era.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Look Back: C/C++ Macros and the Preprocessor
&lt;/h2&gt;

&lt;p&gt;Long before modern JavaScript build pipelines, the C and C++ world lived through a very similar confusion. And it all started with a layer that sat before compilation: the &lt;strong&gt;preprocessor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To understand today’s directives, we need to briefly revisit three pillars of that era — because they map surprisingly well to what we see today.&lt;/p&gt;
&lt;h3&gt;
  
  
  The C Preprocessor Was Not the Language — But It Felt Like It
&lt;/h3&gt;

&lt;p&gt;Consider this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define PI 3.14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many beginners encountering this for the first time assume:&lt;br&gt;
“Oh, C has a special language keyword for defining constants.”&lt;/p&gt;

&lt;p&gt;Except… &lt;code&gt;#define&lt;/code&gt; is not part of the C language grammar.&lt;br&gt;
It’s an instruction to a separate tool that runs before the compiler.&lt;/p&gt;

&lt;p&gt;It performs text substitution — not type checking, not syntax analysis, not semantic validation.&lt;/p&gt;

&lt;p&gt;Yet, because it lived inside &lt;code&gt;.c/.h&lt;/code&gt; files and looked like “code”, generations of developers perceived it as &lt;strong&gt;the language&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;A directive in JS behaves the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&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 JavaScript engine doesn’t know what that means.&lt;br&gt;
The bundler decides what to &lt;em&gt;rewrite&lt;/em&gt; before execution.&lt;/p&gt;

&lt;p&gt;Both are &lt;strong&gt;pre-language phases&lt;/strong&gt; that masquerade as language.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pragmas: The Original “Framework Directives”
&lt;/h3&gt;

&lt;p&gt;If macros were like today’s codegen utilities, then &lt;code&gt;#pragma&lt;/code&gt; was the closest ancestor to modern directives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#pragma once
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line is not part of the C++ language spec.&lt;br&gt;
It’s a compiler-specific “hint” — an instruction that affects how the build system treats this file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No import&lt;/li&gt;
&lt;li&gt;No namespace or provenance&lt;/li&gt;
&lt;li&gt;No indication of ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different compilers supported different pragmas. Some ignored them. Some produced warnings. Some behaved differently.&lt;/p&gt;

&lt;p&gt;Replace “compiler” with “bundler” and you get 2025.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js reacts to &lt;code&gt;'use client'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vite might ignore it&lt;/li&gt;
&lt;li&gt;A custom RSC bundler might interpret it differently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s the same fragmentation pattern seen 30 years ago.&lt;/p&gt;
&lt;h3&gt;
  
  
  Include Guards: The First “Invisible Build-Time Behavior”
&lt;/h3&gt;

&lt;p&gt;Before &lt;code&gt;#pragma once&lt;/code&gt; became common, C/C++ used &lt;strong&gt;include guards&lt;/strong&gt; to prevent double inclusion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifndef MY_HEADER_H
#define MY_HEADER_H
&lt;/span&gt;
&lt;span class="c1"&gt;// header contents&lt;/span&gt;

&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;altered program behavior&lt;/li&gt;
&lt;li&gt;existed only for tooling&lt;/li&gt;
&lt;li&gt;had no runtime meaning&lt;/li&gt;
&lt;li&gt;required knowledge of the &lt;strong&gt;build model&lt;/strong&gt;, not just the language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;C/C++ forced developers to learn that “code you write in a file” and “the language itself” are not the same thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was a painful but transformative lesson.&lt;/p&gt;

&lt;p&gt;We are now at the &lt;strong&gt;same educational moment&lt;/strong&gt; in JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Stage Compilation: A Familiar Pipeline
&lt;/h3&gt;

&lt;p&gt;C/C++ had distinct layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Preprocessor → Compiler → Linker → Executable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modern JS tooling has the &lt;strong&gt;same separation&lt;/strong&gt;, just with cooler names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Directive Scanner / Loader → AST Transforms → Bundler → Output Chunks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like beginners blamed “C++ the language” for preprocessor quirks, today developers blame:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“JavaScript”&lt;/li&gt;
&lt;li&gt;“React”&lt;/li&gt;
&lt;li&gt;“the platform”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…for behaviors that actually originate from build tooling.&lt;/p&gt;

&lt;p&gt;History is repeating itself — almost line for line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern Directives Through the Lens of the Preprocessor Era
&lt;/h2&gt;

&lt;p&gt;Once you see the C/C++ parallels, modern JavaScript directives suddenly make a lot more sense. They are not an evolution of JavaScript syntax — they are an evolution of compiler hints.&lt;/p&gt;

&lt;p&gt;Let’s make the mapping explicit:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Preprocessor Era Concept&lt;/th&gt;
&lt;th&gt;Modern JS Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#pragma&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;'use client'&lt;/code&gt;, &lt;code&gt;'use server'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;#define&lt;/code&gt; macros&lt;/td&gt;
&lt;td&gt;code transforms / auto-generated wrappers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;#include&lt;/code&gt; guards&lt;/td&gt;
&lt;td&gt;module boundary / hydration boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compiler-specific behavior&lt;/td&gt;
&lt;td&gt;bundler-specific behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;multi-stage builds&lt;/td&gt;
&lt;td&gt;loader → transform → bundle pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Just like in the 90s, the surface looks deceptively simple — but the real action happens underneath.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directives Don’t Execute — They Instruct a Tool
&lt;/h3&gt;

&lt;p&gt;A key property of directives is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They don’t do anything &lt;em&gt;themselves&lt;/em&gt; — they only change how something else behaves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s a breakdown of what typically happens in a directive-driven pipeline (simplified, but accurate enough):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read file → Check for directives → Decide environment / boundary →
Run transforms → Generate client/server bundles → Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s compare that to the C pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read file → Preprocessor expands macros + handles pragmas →
Compile → Link → Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are &lt;strong&gt;structurally identical concepts&lt;/strong&gt;, just applied to different languages and eras.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Illusion of a Language Feature Persists
&lt;/h3&gt;

&lt;p&gt;Three psychological factors contribute to the confusion:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They look like syntax&lt;/strong&gt;&lt;br&gt;
Both &lt;code&gt;#pragma once&lt;/code&gt; and &lt;code&gt;'use client'&lt;/code&gt; feel like reserved keywords.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They live at the top of the file&lt;/strong&gt;&lt;br&gt;
Anything that shapes the &lt;em&gt;entire file&lt;/em&gt; is often assumed to be language.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They act globally and implicitly&lt;/strong&gt;&lt;br&gt;
They don’t require instantiation or import.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, directives successfully exploit a &lt;strong&gt;linguistic illusion&lt;/strong&gt;.&lt;br&gt;
They act like a programming language — while not being one.&lt;/p&gt;
&lt;h3&gt;
  
  
  But Modern Directives Go Further Than C Pragmas
&lt;/h3&gt;

&lt;p&gt;Here’s where things get more interesting:&lt;/p&gt;

&lt;p&gt;C pragmas only affected compilation behavior.&lt;br&gt;
Modern directives often affect &lt;strong&gt;execution model, code placement, bundling, and runtime boundaries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;'use server'&lt;/code&gt; might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move code into a server-only chunk&lt;/li&gt;
&lt;li&gt;replace calls with RPC stubs&lt;/li&gt;
&lt;li&gt;add serialization wrappers&lt;/li&gt;
&lt;li&gt;enforce data-flow constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is already beyond what the C preprocessor did.&lt;/p&gt;

&lt;p&gt;It’s closer to a &lt;strong&gt;macro system + compiler pass&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Which is exactly why understanding the distinction matters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If something affects runtime execution and code placement, we should not treat it as “just a string at the top of the file”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And yet, ironically, most misconceptions arise because that is &lt;em&gt;exactly how it appears&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Build Tools Are the Real Interpreter of Directives
&lt;/h3&gt;

&lt;p&gt;Just like different C compilers treated pragmas differently, modern JS tooling varies, too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js interprets &lt;code&gt;'use client'&lt;/code&gt; one way&lt;/li&gt;
&lt;li&gt;Vite + RSC implementations another&lt;/li&gt;
&lt;li&gt;Third-party bundlers a third way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If tomorrow another framework introduced:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;JavaScript engines wouldn’t care.&lt;br&gt;
Tools would decide what it means.&lt;/p&gt;

&lt;p&gt;And that is the mental shift we need developers to make:&lt;/p&gt;

&lt;p&gt;Directive ≠ language&lt;br&gt;
Directive = build instruction&lt;/p&gt;

&lt;p&gt;They live in userland — not in the spec.&lt;/p&gt;

&lt;p&gt;Before we move on, it’s worth pausing for a moment. The parallels with C and C++ are useful for understanding the shape of directives, but they can still feel abstract until you’ve seen them in practice. To bring the idea into clearer focus, let’s look at a concrete example from today’s ecosystem — one that makes the theory a bit more tangible, and shows how these compiler hints manifest in real, modern code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;When Inline Server Functions Reveal the Need for Directives&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I once ran into a case that perfectly exposed why some server behaviors cannot be left to runtime.&lt;/p&gt;

&lt;p&gt;Consider an inline server function defined inside a Server Component, capturing variables from its local scope:&lt;/p&gt;


&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Dashboard&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;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.27&lt;/span&gt;&lt;span class="p"&gt;;&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;save&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="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&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;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ← captures "rate" from the component scope&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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;gt;&lt;/span&gt;Save&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;At first glance, this feels like a normal function call. But for this to work, the bundler must do something subtle and non-negotiable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;detect that save is a server function,&lt;/li&gt;
&lt;li&gt;hoist it into a server-only module,&lt;/li&gt;
&lt;li&gt;perform AST-level closure analysis to capture rate,&lt;/li&gt;
&lt;li&gt;and generate a stable, directly callable reference for the client — not a runtime-constructed wrapper.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A runtime helper cannot solve this.&lt;br&gt;
By the time the code runs, the closure is gone and the intent is already lost.&lt;br&gt;
The function must be transformed before the program exists, so that the client can call it as a direct function, not a proxy we stitched together too late.&lt;/p&gt;

&lt;p&gt;This is why directives fit this space so well: they tell the build tools at the moment of definition what this function truly is, giving them time to shape it accordingly.&lt;/p&gt;

&lt;p&gt;Some decisions must happen while the code is still being woven — not after it is already alive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Educational Gap — and What We Can Learn From History
&lt;/h2&gt;

&lt;p&gt;If you step back and look at the confusion around directives today, you’ll notice something striking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The problem is no longer technical — it’s educational.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’ve repeated a historical pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tool-level construct &lt;strong&gt;looks&lt;/strong&gt; like language&lt;/li&gt;
&lt;li&gt;Developers assume it is language&lt;/li&gt;
&lt;li&gt;The mental model becomes wrong&lt;/li&gt;
&lt;li&gt;Confusion spreads faster than documentation can correct it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This happened with the C preprocessor.&lt;br&gt;
It’s happening again with JavaScript directives.&lt;/p&gt;

&lt;h3&gt;
  
  
  We Need to Teach the Layering — Not Just the Feature
&lt;/h3&gt;

&lt;p&gt;If we teach &lt;code&gt;'use server'&lt;/code&gt; as:&lt;/p&gt;

&lt;p&gt;“This makes your function run on the server.”&lt;/p&gt;

&lt;p&gt;…we’ve already lost.&lt;/p&gt;

&lt;p&gt;Because that sentence hides 4 separate layers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Responsible For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Syntax&lt;/td&gt;
&lt;td&gt;writing a string literal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loader&lt;/td&gt;
&lt;td&gt;detecting the directive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build tools&lt;/td&gt;
&lt;td&gt;transforming / splitting code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;enforcing the boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If developers don’t understand which layer is responsible for what, they will blame “JavaScript” for a bundler problem — just like C developers once blamed “the language” for preprocessor bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the C/C++ Community Eventually Learned
&lt;/h3&gt;

&lt;p&gt;Over time, C/C++ education evolved:&lt;/p&gt;

&lt;p&gt;Early teaching:&lt;br&gt;
“Here’s how to use &lt;code&gt;#define&lt;/code&gt; and &lt;code&gt;#pragma&lt;/code&gt;.”&lt;/p&gt;

&lt;p&gt;Mature teaching:&lt;br&gt;
“Here’s what the &lt;strong&gt;preprocessor&lt;/strong&gt; is, and why it’s separate from the language.”&lt;/p&gt;

&lt;p&gt;After that shift, confusion dropped dramatically. No serious C++ course today teaches macros without first teaching the &lt;strong&gt;mental model&lt;/strong&gt; of the compilation stages.&lt;/p&gt;

&lt;p&gt;We need the same shift for JS directives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directives Aren’t Bad — They’re Powerful, If Understood
&lt;/h3&gt;

&lt;p&gt;This post is not an argument against directives. They serve a purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conveying intent declaratively&lt;/li&gt;
&lt;li&gt;Reducing boilerplate&lt;/li&gt;
&lt;li&gt;Helping tools optimize and separate code&lt;/li&gt;
&lt;li&gt;Giving the developer a simple switch for complex behaviors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In fact, some of the most ergonomic server/serverless features would be far more cumbersome without them.&lt;/p&gt;

&lt;p&gt;But the price of ergonomics is &lt;strong&gt;clarity debt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If we don’t teach where the magic comes from, developers misattribute the source of truth — and debugging collapses.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Simple Mental Model to Teach (Starting Tomorrow)
&lt;/h3&gt;

&lt;p&gt;I like to explain directives with a single sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A directive is a note you leave for your build tools — not for JavaScript.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That line alone fixes 70% of misunderstandings.&lt;/p&gt;

&lt;p&gt;Add one analogy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If &lt;code&gt;#pragma once&lt;/code&gt; was not C++ syntax, then &lt;code&gt;'use client'&lt;/code&gt; is not JavaScript syntax either.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And suddenly, people get it.&lt;/p&gt;

&lt;p&gt;This doesn’t require more docs — it requires better &lt;strong&gt;framing&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Step Toward Clarity: A TypeScript Plugin for Directives
&lt;/h2&gt;

&lt;p&gt;Before wrapping up, I want to share one more concrete step I took to reduce this confusion in real-world codebases. If part of the problem comes from directives &lt;em&gt;looking&lt;/em&gt; like language yet lacking any formal structure, then giving them &lt;strong&gt;type-level meaning&lt;/strong&gt; is one way to bridge the gap.&lt;/p&gt;

&lt;p&gt;I built a small TypeScript plugin called &lt;code&gt;typescript-plugin-directives&lt;/code&gt; that brings &lt;em&gt;type safety and IntelliSense awareness&lt;/em&gt; to directives. It allows teams to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define their own directive vocabulary,&lt;/li&gt;
&lt;li&gt;validate them at compile time,&lt;/li&gt;
&lt;li&gt;get editor hints and autocomplete,&lt;/li&gt;
&lt;li&gt;and avoid silent typos like &lt;code&gt;'use clinet'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn’t to “standardize” directives, but to &lt;strong&gt;make their intent explicit and visible to both developers and tooling&lt;/strong&gt; — without needing a bundler to interpret them first.&lt;/p&gt;

&lt;p&gt;You can try it here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/typescript-plugin-directives" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/typescript-plugin-directives&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/lazarv/typescript-plugin-directives" rel="noopener noreferrer"&gt;https://github.com/lazarv/typescript-plugin-directives&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s intentionally lightweight — just a small layer to help the mental model click earlier, and to give directives a more formal shape inside TypeScript projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There’s a common assumption that a TypeScript plugin could “enforce” directives — that if the plugin knows about them, the system becomes safe by default.&lt;br&gt;
But a TS plugin lives in the Language Service. It can provide awareness, warnings, and guidance — yet it still doesn’t run where code is actually transformed. It doesn’t participate in compilation or bundling.&lt;/p&gt;

&lt;p&gt;A plugin can help identify directive usage, but &lt;strong&gt;it cannot enforce their semantics&lt;/strong&gt;.&lt;br&gt;
For that, the compiler needs a signal of intent — something the type system can understand.&lt;/p&gt;

&lt;p&gt;To enable this, I expose a global &lt;code&gt;Directive&lt;/code&gt; type in my plugin. Authors can use &lt;code&gt;satisfies&lt;/code&gt; not to inform the editor, but to inform the &lt;strong&gt;TypeScript compiler&lt;/strong&gt; that a given string is intended to be a directive:&lt;/p&gt;


&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This doesn’t change behavior or trigger any transformation.&lt;br&gt;
What it does is far simpler and more fundamental:&lt;/p&gt;

&lt;p&gt;It tells the type system:&lt;br&gt;
&lt;strong&gt;“Treat this as a directive — and validate it as such.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It aligns intent with the compiler, not with runtime, and not only with the IDE.&lt;/p&gt;

&lt;p&gt;The actual separation of worlds — hoisting inline server functions out of a component, analyzing captured scope, generating a directly callable boundary — still belongs entirely to the build. The type system can acknowledge intent, but the bundler is the one who must act on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One practical caveat:&lt;/strong&gt; today, some tooling — including Next.js — expects directives to appear as &lt;em&gt;bare string literals&lt;/em&gt;. When written as &lt;code&gt;'use server' satisfies Directive&lt;/code&gt;, the directive may no longer be detected, since the literal is no longer in the exact form the framework scans for. Until this changes, this pattern won’t be picked up by Next.js.&lt;/p&gt;

&lt;p&gt;There is one more subtlety worth mentioning. This type-level intent only matters if a type-checker is actually running. Many modern toolchains — esbuild, SWC, Oxc, Bun, even most Deno and Vite setups — do not type-check at all. They simply strip types and move on. In those environments, the &lt;code&gt;Directive&lt;/code&gt; + &lt;code&gt;satisfies&lt;/code&gt; expression becomes a silent note to the compiler that never had a chance to listen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Closing Reflection
&lt;/h3&gt;

&lt;p&gt;The article from Tanner raises a valuable conversation — one worth having early, before bad habits ossify. I don’t believe the goal should be to eliminate directives; they clearly solve real problems. But we &lt;em&gt;can&lt;/em&gt; learn from history, and avoid the confusion that entire generations of C/C++ developers had to unlearn.&lt;/p&gt;

&lt;p&gt;We’ve been here before.&lt;br&gt;
We know how this story goes.&lt;br&gt;
This time, we can skip the decade of confusion in the middle.&lt;/p&gt;

&lt;p&gt;Teach the layers.&lt;br&gt;
Teach the provenance.&lt;br&gt;
Teach the mental model.&lt;/p&gt;

&lt;p&gt;And directives will stop feeling like “secret language features” — and start feeling like the powerful, intentional compiler hints they actually are.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>react</category>
      <category>reactserver</category>
    </item>
    <item>
      <title>Exploring an experimental Micro-Frontend Architecture with Server-Side Rendering using React Server Components</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Sun, 25 Aug 2024 12:18:46 +0000</pubDate>
      <link>https://dev.to/lazarv/exploring-an-experimental-micro-frontend-architecture-with-server-side-rendering-using-react-server-components-2d0f</link>
      <guid>https://dev.to/lazarv/exploring-an-experimental-micro-frontend-architecture-with-server-side-rendering-using-react-server-components-2d0f</guid>
      <description>&lt;p&gt;&lt;a id="introduction"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;What are Micro-Frontends?&lt;/li&gt;
&lt;li&gt;Introduction to React Server Components (RSC)&lt;/li&gt;
&lt;li&gt;The Experimental Architecture: Combining Micro-Frontends with RSC&lt;/li&gt;
&lt;li&gt;Challenges and Considerations&lt;/li&gt;
&lt;li&gt;The Future of Micro-Frontends with RSC&lt;/li&gt;
&lt;li&gt;Introducing &lt;code&gt;@lazarv/react-server&lt;/code&gt; with RSC Delegation&lt;/li&gt;
&lt;li&gt;Implementing the Experimental Micro-Frontend Architecture with &lt;code&gt;@lazarv/react-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Supporting Client Components in a Micro-Frontend Architecture&lt;/li&gt;
&lt;li&gt;Implementing Server Actions in a Micro-Frontend Architecture&lt;/li&gt;
&lt;li&gt;Using Static Site Generation in a Micro-Frontend Architecture&lt;/li&gt;
&lt;li&gt;Server Islands: loading MFE content using &lt;code&gt;ReactServerComponent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Shared State Management in a Micro-Frontend Architecture&lt;/li&gt;
&lt;li&gt;Leveraging Native ES Modules with &lt;code&gt;@lazarv/react-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In recent years, the micro-frontend architecture has gained significant traction, enabling teams to independently develop and deploy features within a larger application. This paradigm empowers organizations to scale development efforts by decentralizing codebases and fostering collaboration across teams. Coupled with server-side rendering (SSR), micro-frontends offer enhanced performance and SEO capabilities. In this blog post, we'll explore an experimental architecture that marries micro-frontends with React Server Components (RSC), pushing the boundaries of what's possible in modern web development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Micro-Frontends?
&lt;/h2&gt;

&lt;p&gt;&lt;a id="what-are-micro-frontends"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Micro-frontends extend the concept of micro-services to the frontend, where a large application is broken down into smaller, independently deployable pieces. Each micro-frontend can be built and maintained by different teams, possibly using different technologies. This approach allows teams to work in parallel without stepping on each other's toes, thus accelerating the development process and making the codebase more maintainable over time.&lt;/p&gt;

&lt;p&gt;However, this flexibility comes with its own set of challenges. Ensuring consistency in user experience, managing shared state, and optimizing performance across micro-frontends are some of the significant hurdles that developers face.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does This Stack Up?
&lt;/h3&gt;

&lt;p&gt;When considering alternative approaches like traditional monolithic applications or Single Page Applications (SPAs), the micro-frontend architecture offers superior scalability and team autonomy. Unlike monoliths, which can become unwieldy as they grow, micro-frontends allow teams to iterate on specific features independently. Compared to SPAs, this architecture provides better initial load performance through SSR, which is crucial for applications with SEO requirements or those targeting low-bandwidth environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to React Server Components (RSC)
&lt;/h2&gt;

&lt;p&gt;&lt;a id="introduction-to-rsc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Server Components (RSC) is a new feature of React that allows components to be rendered on the server. Unlike traditional SSR, where HTML is generated on the server, React Server Components can return lightweight "descriptions" of UI components, which the client can then render.&lt;/p&gt;

&lt;p&gt;This approach has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance:&lt;/strong&gt; By offloading some rendering logic to the server, the client-side workload is reduced, leading to faster interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code-Splitting at a Component Level:&lt;/strong&gt; RSC facilitates finer-grained code-splitting, allowing for only the necessary components to be loaded and rendered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Developer Experience:&lt;/strong&gt; The seamless integration of server and client components allows for a smoother development experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Experimental Architecture: Combining Micro-Frontends with RSC
&lt;/h2&gt;

&lt;p&gt;&lt;a id="combining-mfe-with-rsc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our experimental architecture attempts to leverage the strengths of both micro-frontends and React Server Components. Here's an overview of the key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Independent Micro-Frontend Applications:&lt;/strong&gt; Each micro-frontend is developed as a standalone application. These applications can be written in different frameworks or technologies but are integrated using a common interface or shared protocol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server-Side Rendering with React Server Components:&lt;/strong&gt; The server takes responsibility for rendering React Server Components, returning lightweight component descriptions to the client. This reduces the initial load time and provides a smoother user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared State Management:&lt;/strong&gt; To ensure consistency across micro-frontends, a shared state management layer is introduced. This layer allows for state synchronization and communication between different micro-frontends, even if they are built using different technologies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lazy Loading and Code Splitting:&lt;/strong&gt; RSC enables lazy loading of components at a granular level, ensuring that only the necessary code is sent to the client. This optimizes performance and reduces the time to interactive (TTI).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges and Considerations
&lt;/h2&gt;

&lt;p&gt;&lt;a id="challenges-and-considerations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While this architecture offers significant benefits, it is not without its challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; The integration of micro-frontends with React Server Components adds a layer of complexity. Teams must be diligent in managing dependencies, ensuring compatibility, and maintaining a consistent user experience across micro-frontends.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Overhead:&lt;/strong&gt; Although server-side rendering improves the initial load time, it can introduce performance overhead on the server. Proper load balancing and caching strategies are crucial to mitigate this issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SEO and Accessibility:&lt;/strong&gt; While SSR improves SEO, developers need to ensure that all micro-frontends adhere to best practices for SEO and accessibility, particularly in a mixed technology environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared Dependencies:&lt;/strong&gt; Managing shared dependencies across micro-frontends can be challenging. Ensuring that different versions of libraries or frameworks do not conflict is crucial to maintaining application stability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Future of Micro-Frontends with RSC
&lt;/h2&gt;

&lt;p&gt;&lt;a id="future-of-mfe-with-rsc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This experimental architecture is just one possible way to harness the power of micro-frontends and React Server Components. As these technologies evolve, we can expect to see more refined patterns and best practices emerge. For now, this approach represents an exciting frontier in web development, offering the potential for highly scalable, performant, and maintainable applications.&lt;/p&gt;

&lt;p&gt;By combining micro-frontends with React Server Components, we can create applications that are not only modular and scalable but also optimized for performance and user experience. However, careful consideration must be given to the inherent complexity and potential pitfalls of this approach.&lt;/p&gt;

&lt;p&gt;To illustrate a potential real-world use case, consider a large e-commerce platform that needs to scale while maintaining high performance. Using the micro-frontend architecture with React Server Components, each part of the application, such as the product catalog, user reviews, and checkout process, could be developed, deployed, and updated independently. This modular approach allows teams to work on different features simultaneously, reduces the risk of errors during deployment, and improves load times through server-side rendering, ultimately enhancing the user experience and streamlining maintenance.&lt;/p&gt;

&lt;p&gt;As always, the best architecture is one that fits your specific use case. If you're considering adopting this experimental approach, weigh the pros and cons carefully and consider starting with a proof of concept to validate your ideas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing &lt;code&gt;@lazarv/react-server&lt;/code&gt; with RSC Delegation
&lt;/h2&gt;

&lt;p&gt;&lt;a id="rsc-delegation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@lazarv/react-server&lt;/code&gt; is a cutting-edge framework designed to streamline the development of micro-frontends with robust server-side rendering capabilities. One of the standout features of this framework is its support for React Server Components (RSC), enabling developers to efficiently manage server-side logic while maintaining a seamless user experience. However, what truly sets &lt;code&gt;@lazarv/react-server&lt;/code&gt; apart is its brand-new feature, &lt;strong&gt;RSC Delegation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RSC Delegation&lt;/strong&gt; is a powerful mechanism that allows developers to delegate specific React Server Components to different micro-frontends or even entirely separate servers. This feature ensures that complex or resource-intensive components can be processed on dedicated servers, optimizing performance and scalability. For instance, if one micro-frontend in your application requires heavy data processing, you can delegate that component's execution to a specialized server, leaving the rest of your application to run smoothly.&lt;/p&gt;

&lt;p&gt;By using &lt;strong&gt;RSC Delegation&lt;/strong&gt;, &lt;code&gt;@lazarv/react-server&lt;/code&gt; enables a more modular and flexible architecture, where each micro-frontend can handle its own server-side rendering tasks independently or delegate them as needed. This not only improves the overall performance of your application but also simplifies the development process, as each component can be optimized for its specific environment without impacting the rest of the system. This feature makes &lt;code&gt;@lazarv/react-server&lt;/code&gt; an even more powerful tool for building scalable, maintainable web applications.&lt;/p&gt;

&lt;p&gt;Check out more about &lt;code&gt;@lazarv/react-server&lt;/code&gt; at &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;https://react-server.dev&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the Experimental Micro-Frontend Architecture with &lt;code&gt;@lazarv/react-server&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a id="implementing-mfe-with-react-server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we've discussed the theoretical aspects and potential benefits of combining micro-frontends with React Server Components, it's time to dive into a practical implementation. For this, we'll be using the &lt;code&gt;@lazarv/react-server&lt;/code&gt; framework — a powerful tool designed specifically for server-side rendering with React Server Components.&lt;/p&gt;

&lt;p&gt;This section will guide you through setting up and implementing this architecture using &lt;code&gt;@lazarv/react-server&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Installing &lt;code&gt;@lazarv/react-server&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;First, you'll need to install the &lt;code&gt;@lazarv/react-server&lt;/code&gt; package. You can do this via pnpm, npm or even yarn. For this example project, we’ll be utilizing pnpm, a fast and efficient package manager that’s particularly well-suited for managing dependencies in complex, multi-project environments like micro-frontends.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
pnpm init
pnpm add @lazarv/react-server 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will add the &lt;code&gt;@lazarv/react-server&lt;/code&gt; package to your project, leveraging pnpm's efficient dependency management system to ensure that your &lt;code&gt;node_modules&lt;/code&gt; folder is optimized and free of duplicates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Hosting Application
&lt;/h3&gt;

&lt;p&gt;We’ll start by creating a hosting application that will serve as the main entry point for rendering and orchestrating different micro-frontends.&lt;/p&gt;

&lt;p&gt;Create a simple React Server Component that will be rendered on the server:&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.jsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HostingApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to the Hosting Application&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;This is a simple React Server Component.&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;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With everything set up, you can run the hosting application directly using the &lt;code&gt;react-server&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm react-server ./index.jsx &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start the development server on &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and automatically open the hosting application in your default web browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Simple Micro-Frontend (MFE) Application
&lt;/h3&gt;

&lt;p&gt;Next, we’ll create a small micro-frontend application that can be loaded into the hosting application.&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;// ./remote.jsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MFE&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;This is the Micro-Frontend Component&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;Loaded from the MFE application.&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;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the MFE application server on port 3001 and open the RSC in the browser using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm react-server ./remote.jsx &lt;span class="nt"&gt;--port&lt;/span&gt; 3001 &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using &lt;code&gt;RemoteComponent&lt;/code&gt; to integrate the MFE Application into the Hosting Application
&lt;/h3&gt;

&lt;p&gt;Finally, we’ll integrate the MFE into the hosting application by using &lt;code&gt;RemoteComponent&lt;/code&gt; from &lt;code&gt;@lazarv/react-server/router&lt;/code&gt; to load the MFE dynamically.&lt;/p&gt;

&lt;h4&gt;
  
  
  Update the Hosting Application to use &lt;code&gt;RemoteComponent&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Modify the &lt;code&gt;HostingApplication&lt;/code&gt; component to include the &lt;code&gt;RemoteComponent&lt;/code&gt; pointing to &lt;a href="http://localhost:3001:" rel="noopener noreferrer"&gt;http://localhost:3001:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./index.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RemoteComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lazarv/react-server/router&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HostingApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to the Hosting Application&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;This is a simple React Server Component.&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="nc"&gt;RemoteComponent&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;"http://localhost:3001"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, the &lt;code&gt;RemoteComponent&lt;/code&gt; will fetch the RSC payload from the MFE application running on &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt; and render it within the hosting application using server-side rendering.&lt;/p&gt;

&lt;p&gt;With both the hosting and MFE applications running, you can now test how they work together. The hosting application will dynamically load and display components from the MFE application, allowing you to see how they integrate in real-time. This setup lets you check if everything is functioning correctly and ensure that the micro-frontends are interacting as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up &lt;code&gt;package.json&lt;/code&gt; scripts
&lt;/h3&gt;

&lt;p&gt;Let's add &lt;code&gt;run-p&lt;/code&gt;, a utility from &lt;code&gt;npm-run-all&lt;/code&gt; that allows you to run multiple npm scripts in parallel. We will also add a script in the &lt;code&gt;package.json&lt;/code&gt; file to start both the hosting and MFE applications simultaneously, while only opening the hosting application in the browser.&lt;/p&gt;

&lt;p&gt;First, navigate to your project’s root directory and install the &lt;code&gt;npm-run-all&lt;/code&gt; package, which includes the &lt;code&gt;run-p&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; npm-run-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll update the package.json file to include the scripts for starting both applications. Here’s how you can do it:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.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;"scripts"&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;"dev:host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server ./index.jsx --name host --open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev:remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server ./remote.jsx --name remote --port 3001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p dev:host dev:remote"&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;"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;"@lazarv/react-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0-experimental-a6c271a-20240825-5a3908ae"&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;"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;"npm-run-all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.1.5"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  In this setup:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dev:host:&lt;/strong&gt; Starts the development server of the hosting application and opens it in the default browser. The &lt;code&gt;--name host&lt;/code&gt; argument prefixes the server logs with "host" for easier identification in the terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dev:remote:&lt;/strong&gt; Starts the development server of the MFE application on port 3001. The &lt;code&gt;--name remote&lt;/code&gt; argument prefixes the logs with "remote" for clarity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dev:&lt;/strong&gt; Uses &lt;code&gt;run-p&lt;/code&gt; to run both &lt;code&gt;dev:host&lt;/code&gt; and &lt;code&gt;dev:remote&lt;/code&gt; in parallel, ensuring both servers start simultaneously but only the hosting application opens in the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the applications
&lt;/h3&gt;

&lt;p&gt;To start both the hosting and MFE applications with the appropriate logging prefixes, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start both servers concurrently. The hosting application will be accessible on &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;, and the MFE application will be on &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;. Only the hosting application will open automatically in the browser, while logs from both applications will be prefixed with "host" and "remote" respectively for clear identification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supporting Client Components in a Micro-Frontend Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a id="client-components-in-mfe"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a micro-frontend setup, client components play a crucial role in maintaining the balance between server-rendered content and client-side interactivity. With React's new "use client" directive, client components are still rendered on the server side, ensuring that the initial content is available as quickly as possible. Once the content is sent to the browser, these components are hydrated, which enables their interactivity, such as handling user input, managing state, and responding to events.&lt;/p&gt;

&lt;p&gt;By incorporating client components into a micro-frontend architecture, you ensure that each micro-frontend not only contributes to the overall performance and SEO of the application but also provides a rich, interactive experience for users. This dual-rendering capability allows developers to choose the right balance of server-side rendering and client-side interactivity, depending on the needs of each component.&lt;/p&gt;

&lt;p&gt;We will demonstrate how to integrate client components into a micro-frontend architecture using &lt;code&gt;@lazarv/react-server&lt;/code&gt;. Specifically, we’ll create a new micro-frontend application that includes a client-side counter component, showing how it can be seamlessly integrated into an existing hosting application. This example will highlight the flexibility and power of combining server-rendered and client-rendered components within a unified architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a new MFE application with a counter
&lt;/h3&gt;

&lt;p&gt;Next, we'll create a simple counter component that will be the MFE application.&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;// ./counter.jsx&lt;/span&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&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="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;Counter Component&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;Current Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Increment&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Decrement&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component uses React's &lt;code&gt;useState&lt;/code&gt; hook to keep track of the count and provides two buttons to increment and decrement the count.&lt;/p&gt;

&lt;p&gt;To differentiate from the previous MFE, we’ll run this new MFE on port 3002.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm react-server ./counter.jsx &lt;span class="nt"&gt;--port&lt;/span&gt; 3002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a script to the &lt;code&gt;package.json&lt;/code&gt; file to start this new MFE application and also update the &lt;code&gt;dev&lt;/code&gt; script to concurrently run the MFE counter application too:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"dev:counter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server ./counter.jsx --port 3002 --name counter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p dev:host dev:remote dev:counter"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run the MFE counter application using the following command too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev:counter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integrate the Counter MFE Application into the Hosting Application
&lt;/h3&gt;

&lt;p&gt;Finally, let's integrate this new MFE into the existing hosting application.&lt;/p&gt;

&lt;p&gt;Modify the &lt;code&gt;HostingApplication&lt;/code&gt; component to include the new &lt;code&gt;RemoteComponent&lt;/code&gt; for the counter MFE:&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.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RemoteComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lazarv/react-server/router&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HostingApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to the Hosting Application&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;This is a simple React Server Component.&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="nc"&gt;RemoteComponent&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;"http://localhost:3001"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RemoteComponent&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;"http://localhost:3002"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, the hosting application will load both the original MFE and the new counter MFE, displaying them on the same page.&lt;/p&gt;

&lt;p&gt;Start the hosting application along with the counter MFE application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this does not work yet! We need to add an import map to resolve the client-side issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is an Import Map?
&lt;/h3&gt;

&lt;p&gt;When working with native ES modules in modern browsers, an import map is a powerful tool that helps you control the way your JavaScript modules are resolved and loaded. By default, when you use the import statement in your code, the browser looks for modules based on their paths or URLs. However, this can become cumbersome when dealing with complex dependencies or when you want to use specific versions of a library without changing all your imports.&lt;/p&gt;

&lt;p&gt;An import map allows you to define custom mappings for module specifiers, essentially telling the browser where to find specific modules. This is particularly useful when you're working with libraries or frameworks that are hosted on a CDN, or when you want to maintain control over the version of a module being used across your project.&lt;/p&gt;

&lt;p&gt;Here's how an import map works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Module Specifier Mapping:&lt;/strong&gt; With an import map, you can map a module specifier (the string you use in the import statement) to a specific URL. This means you can use simple names like &lt;code&gt;react&lt;/code&gt; or &lt;code&gt;lodash&lt;/code&gt; in your imports, and the browser will know exactly where to find them based on your map.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control:&lt;/strong&gt; Import maps allow you to lock your dependencies to specific versions without needing to modify each import statement manually. For example, you can ensure that all references to a library like &lt;code&gt;lodash&lt;/code&gt; point to the same version, which is crucial for maintaining consistency across your application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Imports:&lt;/strong&gt; Instead of dealing with long, complex URLs or paths, you can keep your import statements clean and easy to read. The import map handles the complexity under the hood.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, import maps are a powerful way to manage module resolution in the browser, making it easier to handle dependencies, control versions, and keep your codebase clean and maintainable when using native ES modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Import map definition
&lt;/h3&gt;

&lt;p&gt;To ensure that only a single instance of React is loaded in the browser when working with multiple micro-frontends, it's important to set up an import map. This prevents issues related to having multiple React instances, such as conflicts in state management or hooks not working correctly. Here's how you can add an import map to your project.&lt;/p&gt;

&lt;p&gt;To properly support an import map and ensure that only a single instance of React is loaded across your micro-frontend architecture, you can configure the framework by using a &lt;code&gt;react-server.config.mjs&lt;/code&gt; file. Below is an explanation and a step-by-step guide to implementing this configuration.&lt;/p&gt;

&lt;p&gt;In your project, create a &lt;code&gt;react-server.config.mjs&lt;/code&gt; file. This file will be used to define how modules are resolved and shared across different parts of your application.&lt;/p&gt;

&lt;p&gt;Add the following content to &lt;code&gt;react-server.config.mjs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;importMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react@0.0.0-experimental-58af67a8f8-20240628?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-dev-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react@0.0.0-experimental-58af67a8f8-20240628/jsx-dev-runtime?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom@0.0.0-experimental-58af67a8f8-20240628?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom@0.0.0-experimental-58af67a8f8-20240628/client?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server-dom-webpack/client.browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-server-dom-webpack@0.0.0-experimental-58af67a8f8-20240628/client.browser?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3002/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-dev-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server-dom-webpack/client.browser&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="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 import map is a JSON structure that tells the browser where to find specific modules. In this case, we're specifying that React and related packages should be loaded from &lt;a href="https://esm.sh" rel="noopener noreferrer"&gt;esm.sh&lt;/a&gt;, a popular CDN for ES modules while it can also translate CommonJS packages to native ES modules.&lt;/p&gt;

&lt;p&gt;By pointing to &lt;a href="https://esm.sh" rel="noopener noreferrer"&gt;esm.sh&lt;/a&gt;, all micro-frontends and the hosting application will download and use the same instance of React and related modules directly from the CDN on the client-side. This avoids the common issue of having multiple versions of React in the same application, which can cause hooks to break and state to behave unexpectedly.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;resolve.shared&lt;/code&gt; section ensures that specific modules are shared across all components and micro-frontends, preventing multiple instances from being loaded. This guarantees that any part of your application that imports React, ReactDOM, or related libraries will use the same shared instance from esm.sh.&lt;/p&gt;

&lt;p&gt;Now with the import map defined, we can retry running the apps by using &lt;code&gt;pnpm dev&lt;/code&gt; and it should work now with the counter component working inside the hosting application!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Server Actions in a Micro-Frontend Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a id="server-actions-in-mfe"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Server actions in a micro-frontend architecture represent a powerful pattern for managing server-side logic in a modular and secure way. By defining server actions within individual micro-frontends, developers can keep the server-side operations close to the components that need them, maintaining a clean separation of concerns and enhancing scalability.&lt;/p&gt;

&lt;p&gt;In a typical micro-frontend setup, different parts of the application are developed and deployed independently, each potentially containing its own server-side logic. Server actions enable each micro-frontend to manage its own server-side tasks, such as handling form submissions, interacting with databases, or processing data. These actions are executed entirely on the server, ensuring that sensitive logic is not exposed to the client, which greatly enhances security.&lt;/p&gt;

&lt;p&gt;Moreover, by using server actions within micro-frontends, developers can optimize performance by offloading computationally expensive tasks to the server. This reduces the load on the client, resulting in a more responsive user interface. Since server actions are tied to specific components, they also contribute to a modular architecture where each micro-frontend can be developed, tested, and scaled independently of the others.&lt;/p&gt;

&lt;p&gt;Overall, server actions in a micro-frontend architecture allow for a robust, scalable, and secure application structure. They empower each micro-frontend to independently handle its own server-side logic, while still contributing to a cohesive and unified user experience across the entire application.&lt;/p&gt;

&lt;p&gt;Read more about server actions in the React documentation at &lt;a href="https://react.dev/reference/rsc/server-actions" rel="noopener noreferrer"&gt;https://react.dev/reference/rsc/server-actions&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Server Actions with &lt;code&gt;@lazarv/react-server&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now we will implement another MFE application including a form and we will use a server action to handle form submission while staying in the context of the MFE application even when using the hosting application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a new MFE application with a form and a server action
&lt;/h3&gt;

&lt;p&gt;Next, we'll create an RSC component including a form element and we will handle the form submit action using a server action.&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;// ./form.jsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ServerActionComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;formData&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="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;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Anonymous&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;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;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&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;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the previous counter MFE, we’ll run this new MFE on another a new port again, let's use port 3003 for this one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm react-server ./form.jsx &lt;span class="nt"&gt;--port&lt;/span&gt; 3003
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, add a script to the package.json file to start this new MFE application and also update the &lt;code&gt;dev&lt;/code&gt; script to concurrently run the MFE counter application too:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"dev:form"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server ./form.jsx --port 3003 --cors --name form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p dev:host dev:remote dev:counter dev:form"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run the MFE application using a server action by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev:form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integrate the server action driven MFE into the Hosting Application
&lt;/h3&gt;

&lt;p&gt;Finally, let's integrate this new MFE using a server action into the existing hosting application.&lt;/p&gt;

&lt;p&gt;Modify the &lt;code&gt;HostingApplication&lt;/code&gt; to include the new &lt;code&gt;RemoteComponent&lt;/code&gt; for the form MFE:&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.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RemoteComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lazarv/react-server/router&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HostingApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to the Hosting Application&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;This is a simple React Server Component.&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="nc"&gt;RemoteComponent&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;"http://localhost:3001"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RemoteComponent&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;"http://localhost:3002"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RemoteComponent&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;"http://localhost:3003"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, the hosting application will load all of our MFE applications displaying them on the same page.&lt;/p&gt;

&lt;p&gt;Start the hosting application along with the counter MFE application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a micro-frontend (MFE) application with a server action in the hosting application allows for a seamless and integrated approach to managing server-side logic across different parts of an application. In this setup, each micro-frontend can independently execute server actions, such as processing data or interacting with a database, while being part of a larger, cohesive system. The hosting application orchestrates these micro-frontends, ensuring they work together harmoniously, while the server actions keep sensitive logic secure and optimize performance by handling complex operations on the server. This approach enhances modularity, scalability, and maintainability, allowing the application to grow and evolve efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Static Site Generation in a Micro-Frontend Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a id="ssg-in-mfe"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To incorporate Static Site Generation (SSG) into your micro-frontend (MFE) architecture using &lt;code&gt;@lazarv/react-server&lt;/code&gt;, we can configure one of the micro-frontends to generate static content at build time. This content will then be served as part of the production build. By using the "remote" flag for the root path, we ensure that the MFE's content is statically generated and included in the hosting application.&lt;/p&gt;

&lt;p&gt;Here’s how to extend the existing example to include SSG for one of the micro-frontends:&lt;/p&gt;

&lt;h3&gt;
  
  
  Update the &lt;code&gt;react-server.config.mjs&lt;/code&gt; for SSG
&lt;/h3&gt;

&lt;p&gt;To enable SSG, you'll need to update the &lt;code&gt;react-server.config.mjs&lt;/code&gt; configuration to specify that the root path of the first MFE should be statically generated with the "remote" flag. This ensures that the content is pre-rendered at build time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;importMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&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="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Specify the root path&lt;/span&gt;
        &lt;span class="na"&gt;remote&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="c1"&gt;// Enable the "remote" flag for MFE SSG&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the Micro-Frontend and Hosting Applications for production
&lt;/h3&gt;

&lt;p&gt;Since SSG is only applicable in a production environment, you'll need to build your applications for production. Add the necessary build scripts in your package.json:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"build:host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server build ./index.jsx --outDir .react-server --no-export"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server build ./remote.jsx --outDir .react-server-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:counter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server build ./counter.jsx --outDir .react-server-counter --no-export"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:form"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server build ./form.jsx --outDir .react-server-form --no-export"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s build:host build:remote build:counter build:form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server start --outDir .react-server --port 3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server start --outDir .react-server-remote --port 3001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:counter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server start --outDir .react-server-counter --port 3002 --cors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:form"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server start --outDir .react-server-form --port 3003 --cors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p start:host start:remote start:counter start:form"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We instruct each build to use it's own output directory so we don't use the same output directory for any of the MFE applications and the hosting application. Except the "remote" MFE application we also disable SSG as we don't need that for any other MFE application. We also enable CORS for each MFE application which will be called by the hosting application as the hosting application will send network requests to these MFE applications from &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; which is a different hostname than the MFE is listening on and would raise a CORS error in the browser.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Keep in mind that this is just an example and not the pattern you should follow for building for production. Simulating such a setup is way beyond the scope of this blogpost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Preparing the import map for production use
&lt;/h3&gt;

&lt;p&gt;We need to update the import map of the project to use production versions of dependencies when running the applications in production mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;importMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;imports&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&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="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react@0.0.0-experimental-58af67a8f8-20240628?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-dev-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react@0.0.0-experimental-58af67a8f8-20240628/jsx-dev-runtime?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom@0.0.0-experimental-58af67a8f8-20240628?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom@0.0.0-experimental-58af67a8f8-20240628/client?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server-dom-webpack/client.browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-server-dom-webpack@0.0.0-experimental-58af67a8f8-20240628/client.browser?dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3001/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3003/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react@0.0.0-experimental-58af67a8f8-20240628&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react@0.0.0-experimental-58af67a8f8-20240628/jsx-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom@0.0.0-experimental-58af67a8f8-20240628&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom@0.0.0-experimental-58af67a8f8-20240628/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server-dom-webpack/client.browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-server-dom-webpack@0.0.0-experimental-58af67a8f8-20240628/client.browser&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="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-dev-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react/jsx-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server-dom-webpack/client.browser&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="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;remote&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we detect that &lt;code&gt;NODE_ENV&lt;/code&gt; not equals "production" we will still use the development versions of the required dependencies. But when we use production mode, we need to use production builds of React and related packages too, so we need to remove the &lt;code&gt;?dev&lt;/code&gt; query param from the esm.sh URL of dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execute the production build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will build all applications for production and pre-render the content for the root path of the first MFE and include it in the production build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start the production server
&lt;/h3&gt;

&lt;p&gt;After building, you can start the applications in production mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will serve the statically generated content along with the dynamic content, providing an optimized and pre-rendered user experience.&lt;/p&gt;

&lt;p&gt;By integrating Static Site Generation (SSG) into your micro-frontend architecture, you ensure that key parts of your application, such as the first MFE, are pre-rendered and optimized for performance. The use of the "remote" flag in the &lt;code&gt;react-server.config.mjs&lt;/code&gt; ensures that these components are statically generated during the production build process. This approach not only improves load times but also enhances SEO and provides a better overall user experience by delivering fully rendered content as soon as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expanding Static Site Generation Beyond Micro-Frontends
&lt;/h3&gt;

&lt;p&gt;Incorporating Static Site Generation (SSG) into your micro-frontend architecture significantly enhances performance by pre-rendering key parts of your application. While we focused on enabling SSG for a specific micro-frontend (MFE) in this example, it's important to note that SSG isn't limited to just the micro-frontend applications. You can also apply SSG to specific routes within the hosting application itself. By selectively pre-rendering routes in the hosting application, you can optimize the delivery of content that remains relatively static, improving load times and ensuring that your users receive fully rendered pages immediately upon request. This flexibility allows you to tailor your application’s performance and scalability precisely to the needs of your project, combining the benefits of dynamic rendering where needed with the speed of pre-rendered static content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Islands: loading MFE content using &lt;code&gt;ReactServerComponent&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a id="server-islands"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To demonstrate how you can load content from micro-frontend (MFE) applications in the hosting application using the ReactServerComponent with url and defer props (similar to Astro’s server islands), we’ll set up the architecture so that MFE content is loaded only on the client side, ensuring that it doesn't affect the initial server-side rendering.&lt;/p&gt;

&lt;p&gt;This approach allows you to load micro-frontend content dynamically on the client side, deferring the load until the user interacts with the page or until after the initial server-side render. This method is useful for improving perceived performance and user experience by focusing on essential content first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a new MFE application with Suspense
&lt;/h3&gt;

&lt;p&gt;Let's create another MFE application, but this time let's use Suspense! By using Suspense the MFE application can use a streaming response, providing an initial loading content and then transform the initial MFE content to the content streamed to the client.&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;// ./streaming.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AsyncComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      This is a remote component that is loaded using Suspense. -&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;b&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="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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Streaming&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AsyncComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As usual by now, let's run this MFE using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm react-server ./streaming.jsx &lt;span class="nt"&gt;--port&lt;/span&gt; 3004 &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extend &lt;code&gt;package.json&lt;/code&gt; scripts
&lt;/h3&gt;

&lt;p&gt;Add a script to the package.json file to start this new streaming MFE application and also update the &lt;code&gt;dev&lt;/code&gt; script to concurrently run the streaming MFE application too:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"dev:streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server ./streaming.jsx --port 3004 --name streaming"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p dev:host dev:remote dev:counter dev:form dev:streaming"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server build ./streaming.jsx --outDir .react-server-streaming --no-export"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server start --outDir .react-server-streaming --port 3004 --cors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p start:host start:remote start:counter start:form start:streaming"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run the MFE application using Suspense by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev:streaming
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the streaming MFE Application to the Hosting Application
&lt;/h3&gt;

&lt;p&gt;Modify the &lt;code&gt;HostingApplication&lt;/code&gt; to include the new &lt;code&gt;ReactServerComponent&lt;/code&gt; for the streaming MFE:&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.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RemoteComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lazarv/react-server/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ReactServerComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lazarv/react-server/navigation&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HostingApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to the Hosting Application&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;This is a simple React Server Component.&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="nc"&gt;RemoteComponent&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;"http://localhost:3001"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RemoteComponent&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;"http://localhost:3002"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RemoteComponent&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;"http://localhost:3003"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReactServerComponent&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:3004"&lt;/span&gt; &lt;span class="na"&gt;defer&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup we keep all of our previous MFE applications rendered using server-side rendering in the hosting application using &lt;code&gt;RemoteComponent&lt;/code&gt; while rendering the streaming MFE only on the client by using the &lt;code&gt;ReactServerComponent&lt;/code&gt;. As soon as the component renders on the client it starts fetching the streaming content from the streaming MFE we just created.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@lazarv/react-server&lt;/code&gt; uses the same &lt;code&gt;ReactServerComponent&lt;/code&gt; for hydrating your app on the client side. Only the behavior of the component differs in this case and that the component will fetch it's content from an external source by specifying the &lt;code&gt;url&lt;/code&gt; prop and that external source in this case is the streaming MFE.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can extend this by wrapping the &lt;code&gt;ReactServerComponent&lt;/code&gt; into a component using a &lt;code&gt;MutationObserver&lt;/code&gt; to render the &lt;code&gt;ReactServerComponent&lt;/code&gt; only when it's container becomes visible on the page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now start the hosting application along with the streaming MFE application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using &lt;code&gt;ReactServerComponent&lt;/code&gt; with the &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;defer&lt;/code&gt; props in the hosting application, you can dynamically load and render content from micro-frontends on the client side, similar to Astro's server islands. This approach allows you to optimize your application's performance by prioritizing critical content during the initial server-side render, while still leveraging the power of micro-frontends to deliver additional, modular functionality as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared State Management in a Micro-Frontend Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a id="shared-state-management"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing shared state across multiple micro-frontends is one of the more challenging aspects of a micro-frontend architecture. When building modular applications with independent micro-frontends, ensuring consistent and efficient state management becomes critical. In this section, we’ll explore both backend state management and client-side cross-app state management strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend State Management
&lt;/h3&gt;

&lt;p&gt;Backend state management involves centralizing the application's state on the server. This approach ensures that all micro-frontends access a consistent state, regardless of which part of the application is being used. By keeping the state on the server, you reduce the risk of state divergence and improve synchronization across different parts of your application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server-Side State with APIs:&lt;/strong&gt; In a typical setup, the server maintains the state and exposes it through RESTful or GraphQL APIs. Each micro-frontend can query the server to fetch the current state and update it as needed. For example, if one micro-frontend updates a user profile, that change is immediately reflected when another micro-frontend queries the user data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Actions for State Management:&lt;/strong&gt; Using server actions, as discussed earlier, is another powerful way to manage state. Server actions allow micro-frontends to interact with server-side logic directly. For instance, you might have a server action that updates a shared counter or user session, ensuring that all parts of your application stay in sync.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State Persistence:&lt;/strong&gt; Persisting state on the backend can be handled through databases or in-memory data stores like Redis. These data stores allow you to maintain a global state that can be accessed and modified by any micro-frontend, providing a single source of truth for the application.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Client-Side Cross-App State Management
&lt;/h3&gt;

&lt;p&gt;While backend state management ensures consistency, there are scenarios where sharing state directly on the client side is more efficient, especially for real-time interactions or when minimizing server calls is crucial.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shared State Libraries:&lt;/strong&gt; Libraries like Redux, Jotai or Zustand can be used to manage client-side state that needs to be shared across multiple micro-frontends. By configuring a shared store, different micro-frontends can read from and write to the same state, ensuring that all components reflect the current state of the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;For example, you might have a shared Redux store that holds authentication information, and each micro-frontend can access this store to determine whether a user is logged in and what permissions they have.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Custom Event Systems:&lt;/strong&gt; Implementing a custom event bus is another way to share state across micro-frontends on the client side. This approach involves creating a global event system where micro-frontends can emit and listen for events, allowing them to communicate and synchronize state changes without directly depending on each other.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;For instance, when one micro-frontend updates a cart item, it emits an event that other micro-frontends listen for, allowing them to update the cart UI accordingly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Web Storage:&lt;/strong&gt; Web storage solutions like &lt;code&gt;localStorage&lt;/code&gt;, &lt;code&gt;sessionStorage&lt;/code&gt; or IndexedDB can be used for simpler cross-app state sharing. Micro-frontends can write to and read from these storage mechanisms, ensuring that state persists across page reloads or navigations.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;This approach works well for non-sensitive data that doesn’t need to be immediately synchronized across micro-frontends but still needs to be accessible globally, like user preferences or temporary filters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Combining Backend and Client-Side State Management
&lt;/h3&gt;

&lt;p&gt;In practice, a robust micro-frontend architecture often uses a combination of backend and client-side state management. For critical state that requires consistency across all parts of the application, such as user sessions or permissions, backend state management is essential. However, for performance-sensitive operations or real-time interactions, client-side state management offers flexibility and speed.&lt;/p&gt;

&lt;p&gt;By carefully choosing where and how state is managed, you can ensure that your micro-frontends are both independent and synchronized, providing a seamless user experience across your application. This balance allows for the efficient development and deployment of individual micro-frontends while maintaining the integrity of the overall application state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging Native ES Modules with &lt;code&gt;@lazarv/react-server&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a id="es-modules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fact that &lt;code&gt;@lazarv/react-server&lt;/code&gt; natively supports ES modules through Vite and Rollup is a significant advantage for developers building micro-frontends. Vite’s use of native ES modules during development allows for faster builds and more efficient hot module replacement, making the development process smoother and more responsive. When it comes time to build for production, Rollup optimizes these ES modules, ensuring that your application is as performant as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Native ES Modules Simplify Micro-Frontend Architecture
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streamlined Development with Vite:&lt;/strong&gt; Vite’s use of native ES modules means that during development, each micro-frontend can be built and tested independently with minimal configuration. The development server leverages the browser's native support for ES modules, loading dependencies directly as needed. This leads to faster development cycles, as changes are reflected almost instantly without the need for complex bundling steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimized Production Builds with Rollup:&lt;/strong&gt; Rollup takes the ES module-based development environment created by Vite and produces optimized bundles for production. These bundles are smaller, more efficient, and easier to manage, which is especially important in a micro-frontend architecture where multiple independently developed frontends need to come together seamlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Dependency Management:&lt;/strong&gt; Since &lt;code&gt;@lazarv/react-server&lt;/code&gt; uses native ES modules, managing dependencies across different micro-frontends becomes much simpler. Each micro-frontend can specify and import only the dependencies it needs without worrying about global conflicts or redundant code. This also enables you to take advantage of tree-shaking, where unused code is automatically removed during the build process, further optimizing your application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Modularity and Flexibility:&lt;/strong&gt; With native ES modules, the architecture of your micro-frontends becomes more modular. Each micro-frontend is essentially a self-contained unit that can be developed, tested, and deployed independently. This modularity is enhanced by the fact that Vite and Rollup are designed to work seamlessly with ES modules, allowing for easy integration of new features and components without disrupting the overall application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster Builds and Deployments:&lt;/strong&gt; The combination of Vite and Rollup enables quicker build times and more efficient deployments. Vite's instant server start and hot module replacement make development faster, while Rollup's tree-shaking and code-splitting capabilities ensure that the final production bundles are as lean as possible. This is particularly beneficial in a micro-frontend environment, where minimizing build and deployment times is crucial for maintaining agility.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The built-in use of native ES modules in &lt;code&gt;@lazarv/react-server&lt;/code&gt;, facilitated by Vite and Rollup, significantly enhances the development and deployment process for micro-frontends. This approach not only simplifies dependency management and module bundling but also ensures that each micro-frontend can operate independently while still contributing to a cohesive and performant overall application. By leveraging these tools, &lt;code&gt;@lazarv/react-server&lt;/code&gt; offers a streamlined, modular, and efficient architecture that is well-suited to the demands of modern web development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;a id="final-thoughts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Working with &lt;code&gt;@lazarv/react-server&lt;/code&gt; to build a micro-frontend architecture has been an exciting journey, showcasing the framework's potential to revolutionize how we approach modular web development. This framework offers a unique blend of server-side rendering, dynamic content loading, and modular front-end development, making it a powerful tool for creating scalable and maintainable applications.&lt;/p&gt;

&lt;p&gt;However, it's important to acknowledge that &lt;code&gt;@lazarv/react-server&lt;/code&gt; is still in an experimental phase. While the current capabilities are impressive, there are numerous opportunities for refinement and enhancement. For instance, simplifying the API and improving tooling support could make the framework even more accessible to developers. Additionally, as more developers start using it, we can expect the community to drive innovations that further optimize performance and streamline the development process.&lt;/p&gt;

&lt;p&gt;One of the most exciting aspects of &lt;code&gt;@lazarv/react-server&lt;/code&gt; is its extendability. The architecture is designed to adapt and grow alongside your application’s needs. Whether you’re looking to incorporate advanced caching strategies, integrate with modern deployment pipelines, or explore new rendering techniques, the framework is flexible enough to accommodate these ambitions.&lt;/p&gt;

&lt;p&gt;In summary, while &lt;code&gt;@lazarv/react-server&lt;/code&gt; is a powerful tool with a promising future, it’s also a work in progress. As the framework evolves, it will undoubtedly continue to push the boundaries of what’s possible in web development, offering new ways to build fast, scalable, and maintainable applications. The journey with &lt;code&gt;@lazarv/react-server&lt;/code&gt; is just beginning, and there’s a lot to look forward to as it matures and expands its capabilities.&lt;/p&gt;

</description>
      <category>rsc</category>
      <category>react</category>
      <category>ssr</category>
      <category>microfrontend</category>
    </item>
    <item>
      <title>The Hidden Economy of Technology: How Tech's Most Difficult Tools Fuel an Industry</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Wed, 14 Aug 2024 20:03:37 +0000</pubDate>
      <link>https://dev.to/lazarv/the-hidden-economy-of-technology-how-techs-most-difficult-tools-fuel-an-industry-446m</link>
      <guid>https://dev.to/lazarv/the-hidden-economy-of-technology-how-techs-most-difficult-tools-fuel-an-industry-446m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the fast-paced world of technology, complexity is often seen as a necessary evil—a byproduct of sophisticated systems designed to solve challenging problems. But what if complexity isn't just an unintended consequence? What if, in some cases, it drives a broader ecosystem that benefits more than just the engineers who master it? From consultants and content creators to corporate strategies and niche specializations, the intricate dance with complexity has created a thriving economy all its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Complexity in Technology
&lt;/h2&gt;

&lt;p&gt;At first glance, complexity in technology might seem like an obstacle—something to be overcome or simplified. But a deeper look reveals that this complexity often fuels an entire industry, creating opportunities for economic gain, educational advancement, and career specialization.&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%2Fqtibcxne5micofxpt4jb.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%2Fqtibcxne5micofxpt4jb.jpg" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Consultancy Boom: Turning Complexity into Profit
&lt;/h3&gt;

&lt;p&gt;Complex technologies demand expertise. This need has given rise to a robust consultancy market, where businesses and organizations seek out specialists who can navigate these intricate systems. Whether it’s implementing a new enterprise resource planning (ERP) system, optimizing a cloud infrastructure, or managing a complex cybersecurity framework, consultants are in high demand. The more complex the technology, the higher the demand—and the higher the fees.&lt;/p&gt;

&lt;p&gt;Consultants don't just solve problems; they offer peace of mind. Companies are willing to pay a premium for the assurance that their complex systems are in capable hands, making consultancy a lucrative field for those who have mastered the intricacies of these technologies.&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%2Fchoimr2trh8flmvkbmlj.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%2Fchoimr2trh8flmvkbmlj.jpg" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Education and Training: A Growing Industry
&lt;/h3&gt;

&lt;p&gt;The complexity of modern tools and systems has also spurred a booming market in education and training. Companies, universities, and independent platforms like Udemy and Coursera offer specialized courses aimed at demystifying these technologies. Certifications have become badges of honor in the tech world, validating one's ability to navigate and master complex systems.&lt;/p&gt;

&lt;p&gt;For those looking to enter the tech industry or advance their careers, these training programs are invaluable. But they’re also big business. The more difficult the subject, the more willing individuals and companies are to invest in education. This has led to a proliferation of training programs, boot camps, and online courses designed to make complexity more manageable—while also turning a profit.&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%2Fndch6xbxju8kxx5hz93k.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%2Fndch6xbxju8kxx5hz93k.jpg" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Content Creation: Complexity as a Content Goldmine
&lt;/h3&gt;

&lt;p&gt;In the age of digital content, complexity is a goldmine for creators. YouTube channels, blogs, and podcasts that focus on teaching complex technologies attract dedicated audiences eager to learn. These platforms provide tutorials, tips, and deep dives into the nuances of difficult tools, all while generating income through ads, sponsorships, and paid subscriptions.&lt;/p&gt;

&lt;p&gt;For content creators, the appeal is clear: the more complex the subject matter, the more valuable their content becomes. A niche audience of dedicated learners often means higher engagement, more views, and a steady stream of income. Platforms like Patreon have further enabled creators to monetize their expertise, offering exclusive content and one-on-one mentoring for a fee.&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%2F21mreubdjpxpasvxej5n.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%2F21mreubdjpxpasvxej5n.jpg" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Job Market Dynamics: Complexity as a Career Booster
&lt;/h3&gt;

&lt;p&gt;Mastering complex technologies can be a significant career booster. The scarcity of experts in a particular field means that those who do possess the necessary skills are highly sought after. This demand often translates into higher salaries, better job security, and more opportunities for advancement.&lt;/p&gt;

&lt;p&gt;In some cases, entire careers are built around niche specializations in complex technologies. These specialists become indispensable within their organizations, offering expertise that few others possess. The more intricate the technology, the more valuable the specialist, creating a virtuous cycle where complexity drives career growth.&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%2Fh6u1pbbb1izq01mi704f.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%2Fh6u1pbbb1izq01mi704f.jpg" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Corporate Strategy: Leveraging Complexity for Competitive Advantage
&lt;/h3&gt;

&lt;p&gt;Corporations are not blind to the power of complexity. In some cases, companies might intentionally design their systems or technologies to be complex as a form of competitive advantage. This can lead to vendor lock-in, where the difficulty of switching to a different system ensures long-term contracts and ongoing revenue streams.&lt;/p&gt;

&lt;p&gt;Furthermore, the development of complex systems often results in the creation of intellectual property (IP) and patents. These assets can give companies a significant edge in the market, creating barriers to entry for competitors and establishing the company as a leader in a particular technology.&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%2Fulsz6jyud41o7ovbgmli.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%2Fulsz6jyud41o7ovbgmli.jpg" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The Perpetuation of Complexity: A Self-Sustaining Cycle
&lt;/h3&gt;

&lt;p&gt;Over time, complex technologies often become entrenched as legacy systems, further perpetuating their complexity. As these systems evolve, they accumulate additional layers of features and requirements, making them even more difficult to master. This perpetuation creates a continuous need for education, training, and consultancy, sustaining the ecosystem that has grown around the technology.&lt;/p&gt;

&lt;p&gt;Ironically, the complexity itself can drive demand for tools and services that simplify or abstract away that complexity. This has led to the rise of frameworks, platforms, and services that aim to make complex technologies more accessible—yet often become complex themselves as they grow.&lt;/p&gt;

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

&lt;p&gt;Complexity in technology is not just a challenge to be overcome; it’s a driver of economic opportunity and industry growth. From consultancy and education to content creation and corporate strategy, the intricate nature of modern tools fuels a multi-faceted ecosystem that benefits a wide range of stakeholders. While this complexity may frustrate users, it also creates a thriving market for those who are willing to embrace it.&lt;/p&gt;

&lt;p&gt;In the end, the relationship between complexity and opportunity is symbiotic. As technology continues to advance, this hidden economy of complexity is likely to grow, offering new avenues for innovation, specialization, and economic gain. Whether you’re a consultant, a content creator, or a tech professional, understanding and navigating this complexity can be your key to success in the ever-evolving world of technology.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: This blog post was generated with the assistance of ChatGPT, an AI language model developed by OpenAI. This post is part of a test to gauge viewership and engagement. The content reflects a combination of AI-generated ideas and human curation. We appreciate your feedback and interest in this experiment!&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Migrating a Vite + React app to use React Server Components</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Sun, 11 Aug 2024 12:25:11 +0000</pubDate>
      <link>https://dev.to/lazarv/migrating-a-vite-react-app-to-use-react-server-components-507j</link>
      <guid>https://dev.to/lazarv/migrating-a-vite-react-app-to-use-react-server-components-507j</guid>
      <description>&lt;p&gt;Vite has the best developer experience a web developer could wish for. When you want to create a single page application using React then Vite is your default choice. It's incomparable how much better it is than using &lt;code&gt;create-react-app&lt;/code&gt; just a few years ago. Who really misses Webpack? Who misses complex configurations?&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Starting a new Vite + React app is easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create vite my-react-app &lt;span class="nt"&gt;--template&lt;/span&gt; react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you just need to change directory, install dependencies and start your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-react-app
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It just works!&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%2Fqxvg71davn5rx7t4fk55.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%2Fqxvg71davn5rx7t4fk55.png" alt="Image description" width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can achieve great things working only on the client side, what Kent C. Dodds is presenting in this great video is awesome and perfectly viable in a lot of use cases:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/rPjj6s7VPQM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But is it really enough? Probably it is most of the time, but everyone wants server-side rendering nowadays, the hype is real and we want it too, so let's continue...&lt;/p&gt;

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

&lt;p&gt;The hype around RSCs are still rising! More and more frameworks are starting to support the new server-side rendering features of the upcoming new major release of React.&lt;/p&gt;

&lt;p&gt;Surely Next.js was the first to jump on the train (they built it for themselves) and Vercel started to teach developers around the world about the benefits of using RSCs, including how to work with client components and server actions too.&lt;/p&gt;

&lt;p&gt;Other major React based frameworks are picking up RSCs a bit slower though. Remix just demonstrated using RSCs in the loader, while not missing working client components:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZcwA0xt8FlQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Astro is more likely a bit off here as while it supports React, it's implementing it's own similar solutions, like Astro Actions:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/VkYQMhit_04"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;RedwoodJS also works on RSC support and Waku supports the new features already.&lt;/p&gt;

&lt;p&gt;You can also start building your own framework from scratch or using vinxi. But how many developers are up to do this? It's not even really documented how to do it! What about web developers who are not experienced enough or don't have the time to go down this road? Surely there are some, like Hiroshi implementing an RSC supprting framework using both the current Vite API at &lt;a href="https://github.com/hi-ogawa/vite-plugins/tree/main/packages/react-server" rel="noopener noreferrer"&gt;https://github.com/hi-ogawa/vite-plugins/tree/main/packages/react-server&lt;/a&gt; and using the incoming Vite 6 Environment API at &lt;a href="https://github.com/hi-ogawa/vite-environment-examples/tree/main/examples/react-server" rel="noopener noreferrer"&gt;https://github.com/hi-ogawa/vite-environment-examples/tree/main/examples/react-server&lt;/a&gt;. Also there's a great article from Daniel Nagy about building your own framework using Vite and RSC support at &lt;a href="https://danielnagy.me/posts/Post_usaivhdu3j5d" rel="noopener noreferrer"&gt;https://danielnagy.me/posts/Post_usaivhdu3j5d&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But we are drifting too far away from the original simplicity and easy start we had. How to enhance your simple Vite + React single page application with modern server-side rendering?&lt;/p&gt;

&lt;p&gt;A bit controversial tutorial you can find is from Vercel, presented by Lee Robinson:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/AdkNcFUsRQQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But surely a lot of us were very disappointed when the starting Vite client-side app became a heavy Next.js app, removing all traces of Vite in the end. But we adore Vite here right? We love how simple it is, how fast it is, how easy it is to extend it using plugins, especially compared to custom Webpack plugins.&lt;/p&gt;

&lt;p&gt;Almost all other frameworks are using Vite now, especially non-React frameworks, like SvelteKit. When you stick with React, Remix is closest to what we wish for and it will be even more when there will be proper RSC support in Remix. You can already use Remix with Vite and it's great! But it's not supporting RSCs yet and you will get some magic files which will be mostly just deleted as it's not likely you want to modify these. Maybe in a couple months, but expect refactoring!&lt;/p&gt;

&lt;p&gt;Is there another choice? A new alternative? Which framework to choose when you want to modify the starting Vite + React app the least and be able to use everything new from React 19?&lt;/p&gt;

&lt;h2&gt;
  
  
  Vite Forever
&lt;/h2&gt;

&lt;p&gt;So let's correct what Lee did and don't go into the wrong direction by migrating over to Next.js and Webpack, let's stick with Vite at least for a few years, maybe even a decade (or more)!&lt;/p&gt;

&lt;p&gt;As we want to use RSCs, let's move everything from the &lt;code&gt;index.html&lt;/code&gt; into &lt;code&gt;src/main.jsx&lt;/code&gt; and instead of rendering &lt;code&gt;App&lt;/code&gt; on the client-side, just use it as a component in this RSC layout:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&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;head&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;meta&lt;/span&gt; &lt;span class="na"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/vite.svg"&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;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&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;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Vite + React&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&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;head&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;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&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;body&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;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beware that you need to fix &lt;code&gt;charSet&lt;/code&gt; as it has a different casing when used in JSX than in HTML!&lt;/p&gt;

&lt;p&gt;For now, just add a &lt;code&gt;"use client";&lt;/code&gt; directive to &lt;code&gt;App.jsx&lt;/code&gt; to make it work instantly as the App component includes a &lt;code&gt;useState&lt;/code&gt; React hook.&lt;/p&gt;

&lt;p&gt;Now let's uninstall &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; as we will not need them, both were just React 18. Now what? How we will run our app?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @lazarv/react-server@latest 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, I'm installing my own React meta-framework to solve the problem I have here. Is it a better choice than Remix? If you want to use the latest React features, then yes. As it supports RSCs, client components and server actions already, while using Vite, ES modules and keeping everything stupid simple. It's probably best for quick prototyping and for indie devs loving to work with bleeding edge experimental tech. The fact that it was already good enough to implement its own documentation site, using its static-site generation capability, shows great potential. With time, it will become even better and more mature, offering an even wider feature set to work with. You can read more about the framework at &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;https://react-server.dev&lt;/a&gt;. Get back to our app now!&lt;/p&gt;

&lt;p&gt;Just make some more changes on how to start our app:&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;"scripts"&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;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server ./src/main.jsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server build ./src/main.jsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-server start"&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;If you think this is very familiar to how you would use Node.js to run a script, you're right! &lt;code&gt;react-server ./src/main.tsx&lt;/code&gt; is the same as executing &lt;code&gt;node server.js&lt;/code&gt;, just for React RSCs. If it would be enough, we could just run a single RSC component using &lt;code&gt;npx&lt;/code&gt; with this framework, even without installing anything! Just like with Node.js!&lt;/p&gt;

&lt;p&gt;Let's start our app using &lt;code&gt;pnpm dev --open&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%2Fq5ghtviusmyckghjimxz.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%2Fq5ghtviusmyckghjimxz.png" alt="Image description" width="800" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It just works! Awesome! But can we fix the loading glitch that we can see on refreshing the page? What causes this?&lt;/p&gt;

&lt;h1&gt;
  
  
  Optimizing client components
&lt;/h1&gt;

&lt;p&gt;In the current state of our app, the whole &lt;code&gt;App&lt;/code&gt; component is a client component. This is not ideal, as only the counter part is interactive in it. We also attached some CSS to this client component which will only be loaded when the client component loads in the browser at runtime. This is causing a glitch for a blink of an eye at page load.&lt;/p&gt;

&lt;p&gt;Let's create a new &lt;code&gt;Counter&lt;/code&gt; component in a new file at &lt;code&gt;src/Counter.jsx&lt;/code&gt; and move the counter button into this new component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;count&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;count&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      count is &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&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;button&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;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's get back to the &lt;code&gt;App&lt;/code&gt; component, remove the &lt;code&gt;"use client";&lt;/code&gt; directive, remove the &lt;code&gt;App.css&lt;/code&gt; import and the code which was only part of the &lt;code&gt;Counter&lt;/code&gt; component, but import it instead:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;reactLogo&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;./assets/react.svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;viteLogo&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;/vite.svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Counter&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;./Counter.jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&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;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://vitejs.dev"&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;viteLogo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo"&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;"Vite logo"&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;a&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;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://react.dev"&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reactLogo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo react"&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;"React logo"&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;a&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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Vite + React&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Counter&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;
          Edit &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;src/App.jsx&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; and save to test HMR
        &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;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"read-the-docs"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Click on the Vite and React logos to learn more
      &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;/&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;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The easiest resolution of the CSS issue is to import &lt;code&gt;App.css&lt;/code&gt; in  &lt;code&gt;main.jsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// we don't need to do any change here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now both CSS files will be loaded instantly on page load and there will be no flickering. The counter button will still work as before as we refactored it to be a new client component. Every other part of the &lt;code&gt;App&lt;/code&gt; component was static, which is best to be included in RSCs. You can also combine the 2 CSS files as there's no reason why we have two, just fix the CSS imports in the &lt;code&gt;main.jsx&lt;/code&gt; after that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go?
&lt;/h2&gt;

&lt;p&gt;Now we have the same Vite + React app working with server-side rendering. But how to evolve from this state further?&lt;/p&gt;

&lt;p&gt;You can start implementing a form using server actions, just as described at &lt;a href="https://react.dev/reference/rsc/server-actions" rel="noopener noreferrer"&gt;https://react.dev/reference/rsc/server-actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can add a router, maybe a file-system based routing solution supporting RSCs, like &lt;code&gt;@lazarv/react-server-router&lt;/code&gt;. It's very flexible!&lt;/p&gt;

&lt;p&gt;You can export your app as static as there might be no reason to use dynamic server-side rendering and it's enough for you to render your app at build time.&lt;/p&gt;

&lt;p&gt;You can add any Vite plugin and remain in this wonderful ecosystem, extending your app at any time.&lt;/p&gt;

&lt;p&gt;You can eagerly wait for a version of Vite using Rolldown, to have an even more blazing-fast developer experience!&lt;/p&gt;

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

&lt;p&gt;While RSCs are with us for quite some time now, we are still learning how to use them properly. It's a super-power to have and it will certainly evolve further. But it's more like a beast to tame than an easy to understand feature. We are holding endless possibilities in our hands, but most of us don't know what to do with it. With great power comes great responsibility! But even when we tend to over-engineer even extremely simple problems, we like to work with simple technologies. That's how Vite won and how PHP is still with us. It's easy to use. A lot of us are not god-like developers implementing anything from scratch in a day. We are just mere humans trying hard to make some silly things work. And we fail a lot. But we win in the end with persistence, like long-distance runners.&lt;/p&gt;

&lt;p&gt;We should stick to what we already learned in the past. We should not forget, that we already knew how to make things efficiently. We had worse tools, worse tech, worse machines, but it was possible because of creativity and endurance to create wonderful things. Now we should use our new tools, new tech, new machines and we should still use our creativity and imagination to create even more fascinating experiences. We are responsible for making people happy to use our products. Even as software engineers.&lt;/p&gt;

</description>
      <category>vite</category>
      <category>react</category>
      <category>rsc</category>
    </item>
    <item>
      <title>@lazarv/react-server vs Next.js</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Sun, 07 Jul 2024 17:12:26 +0000</pubDate>
      <link>https://dev.to/lazarv/lazarvreact-server-vs-nextjs-ok8</link>
      <guid>https://dev.to/lazarv/lazarvreact-server-vs-nextjs-ok8</guid>
      <description>&lt;p&gt;In this article we will compare &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;@lazarv/react-server&lt;/a&gt; to Next.js as these React meta-frameworks are very close because both are based on the new React 19 server-side rendering features.&lt;/p&gt;

&lt;p&gt;We will investigate both frameworks on their approach and architectural choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a meta-framework?
&lt;/h2&gt;

&lt;p&gt;We can consider a solution to be a meta-framework when the following features are supported and the framework is using a specific library as it's base. Which is React in our case.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File-based routing&lt;/li&gt;
&lt;li&gt;Static generation&lt;/li&gt;
&lt;li&gt;Hybrid rendering&lt;/li&gt;
&lt;li&gt;Data fetching&lt;/li&gt;
&lt;li&gt;Isomorphic component trees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The React team suggests to use a framework for future React development. Next.js, Remix, Gatsby and Expo are their suggestion, while mentioning Next.js App Router as the bleeding-edge framework. If you really want to use React without a framework, you can still use a bundler like Vite or Parcel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js
&lt;/h2&gt;

&lt;p&gt;Originally released in 2016 and nowadays it's the go-to meta-framework for React. There are some developers from the Next.js team who are also involved in the development of React itself. This means that Next.js is following the architectural choices the React team considers the best for React. This could be true for certain use cases, but still it's debatable. One major issue with Next.js is that  it's optimized for the Vercel platform in every single way. While you can run your Next.js app in every environment which supports node.js, making this choice removes or at least diminishes a lot of nice and valuable features from your Next.js app, which could use Vercel platform features to implement these app features in the most optimal way. You can think of image optimalization, ISR, caching, etc.&lt;/p&gt;

&lt;p&gt;Next.js is using Webpack under the hood to bundle your app. While there's experimental support for Turbopack and support for using SWC and Lightning CSS as the compiler. Next.js is still using CommonJS (with some support for ES modules), while the node.js community is mostly trying to switch over to native ES modules and CommonJS is considered legacy now, with some exceptions. Support will remain for years for sure, but there are already npm packages which only support ES modules since a specific version.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;@lazarv/react-server&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In contrast to Next.js, &lt;code&gt;@lazarv/react-server&lt;/code&gt; is using Vite for it's development server and as using Vite, it's bundling for production using Rollup. With this choice it's fully supporting native ES modules. This framework is not favoring any cloud providers like Vercel, but supports deployment to Vercel. As the framework is more of a wrapper around React 19 server-side rendering features without any strict choices on how to implement specific features, it's easier to shape your app the way you want it to work and behave. The only exception is that it will use Vite. Based on that, it's also easier to run apps literally anywhere where you can run a node.js service. The framework also supports cluster mode, which creates a node.js cluster for your app which is beneficial if you run your app in an environment where you want to fully utilize the available resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared features
&lt;/h2&gt;

&lt;p&gt;As both &lt;code&gt;@lazarv/react-server&lt;/code&gt; and Next.js is using React at it's core, both share a lot of features which are provided by React and are not Next.js nor &lt;code&gt;@lazarv/react-server&lt;/code&gt; features directly.&lt;/p&gt;

&lt;p&gt;React Server Components is a hot topic in the community and it's a very versatile tool. It's surely is the next most different new feature in React 19 since hooks. RSCs are rendered only on the server-side and have no client code so you can't re-render these on the client.&lt;/p&gt;

&lt;p&gt;To open a door from RSCs into the client code, you need to create client components. This type of components are basically the same as you already used to in a React server-side rendered app. These are also rendered on the server, but getting hydrated on the client and these are able to re-render on the client based on their state. You can mix RSCs and client components in every way. RSCs can contain client components and client components can also contain RSCs. It's absolutely up to the developer how the app is structured and which part of the app is using RSCs and which part is created using client components and are also running in the browser.&lt;/p&gt;

&lt;p&gt;Using RSCs you are able to load initial data into your client components. To use mutation, you can use server actions. This feature is like a built-in RPC into React and the React meta-frameworks. You can call a server action using HTML form or button elements or by passing the server action as a reference to the client component and calling it like a normal async JavaScript function.&lt;/p&gt;

&lt;p&gt;While with Next.js you have to use it's built-in file-based router named the App Router, when you're using &lt;code&gt;@lazarv/react-server&lt;/code&gt; it's only up to you if you want to use the optional file-based router or you want to implement your application using another solution. If you still need the file-based router, both framework's solution for the router is very similar and if you used Next.js App Router, it will be easy to do the same using &lt;code&gt;@lazarv/react-server-router&lt;/code&gt;. It's just optional. Both framework's routing solution supports middlewares and API routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;All frameworks provide a project creation command line tool for developers to help getting started with it. Next.js has &lt;code&gt;npx create-next-app&lt;/code&gt;, Remix has &lt;code&gt;npx create-remix&lt;/code&gt;, etc. All of these CLI tools create a new project, initializing files required to run your app using that framework and it's also installing required dependencies. These are mostly marketed as a 5min task. Which is fair. These tools are easy to use and it's really just a few minutes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@lazarv/react-server&lt;/code&gt; is different. You don't need any tools to initialize your app. You just need &lt;code&gt;@lazarv/react-server&lt;/code&gt;. After installing it as a dependency or just using &lt;code&gt;npx&lt;/code&gt; to run the framework's CLI, you're done. It brings back the most simple developer experience ever: &lt;code&gt;node server.js&lt;/code&gt;. You already have a JSX file with a component? Just run &lt;code&gt;npx @lazarv/react-server ./App.jsx&lt;/code&gt;. You don't need to install React. It would be pointless and Next.js is also including it's own specific version of React as for all React 19 and experimental React features to work properly, you need a specific version of React. &lt;code&gt;@lazarv/react-server&lt;/code&gt; installs it's own React too. So running your first app with &lt;code&gt;@lazarv/react-server&lt;/code&gt; takes a few seconds. Nothing more. But infinitely expandable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some numbers
&lt;/h2&gt;

&lt;p&gt;Next.js can be slow. This might not feels that much until you experienced something better and &lt;code&gt;@lazarv/react-server&lt;/code&gt; wins by a lot in this. You can experience the same when you migrated your old Create React App project to Vite.&lt;/p&gt;

&lt;p&gt;Our benchmark app might feel weak as it's only the most simple React app possible, but measuring the difference even in this most minimal use case means that Next.js will always lag behind because it's heavy-weight architecture. This is not achieved by using a faster bundler, only with a more suiting architecture.&lt;/p&gt;

&lt;p&gt;Our tiny app looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&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;body&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="p"&gt;&amp;gt;&lt;/span&gt;Hello World!&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;body&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;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Next.js a mock layout file also needs to be created, just rendering &lt;code&gt;children&lt;/code&gt; and doing nothing else.&lt;/p&gt;

&lt;p&gt;The results seem devastating:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Next.js&lt;/th&gt;
&lt;th&gt;&lt;code&gt;@lazarv/react-server&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;startup&lt;/td&gt;
&lt;td&gt;~800ms&lt;/td&gt;
&lt;td&gt;~150ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compiling&lt;/td&gt;
&lt;td&gt;~600ms&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;page load&lt;/td&gt;
&lt;td&gt;~150ms&lt;/td&gt;
&lt;td&gt;~30ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;~3.5s&lt;/td&gt;
&lt;td&gt;~200ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;start&lt;/td&gt;
&lt;td&gt;~100ms&lt;/td&gt;
&lt;td&gt;~100ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rps&lt;/td&gt;
&lt;td&gt;~9k&lt;/td&gt;
&lt;td&gt;~21k&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using the cluster mode available for the production runtime of &lt;code&gt;@lazarv/react-server&lt;/code&gt; you can achieve even more blazing-fast performance even without caching. Using an 8 core M3 Macbook Air it's hitting a whopping ~50k requests per second using all the available CPU cores, so running 8 workers!&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Let's get a look on each type of result and the reason behind the numbers!&lt;/p&gt;

&lt;h4&gt;
  
  
  startup
&lt;/h4&gt;

&lt;p&gt;This is the startup time for the development server. Next.js startup is more than 4x. Next.js is much more heavy and core codebase of Next.js is way too much for an app like this, while &lt;code&gt;@lazarv/react-server&lt;/code&gt; is a light-weight server with being a wrapper around Vite and React where no unnecessary code is run on startup.&lt;/p&gt;

&lt;h4&gt;
  
  
  compiling
&lt;/h4&gt;

&lt;p&gt;Next.js needs to create a bundle for your app. But using Vite, &lt;code&gt;@lazarv/react-server&lt;/code&gt; eliminates this part by not creating any bundle during development. The underlying bundler in case of Vite doesn't need to collect and compile a bundle before you could use your app. This is an essential architectural choice with Vite. Learn more about this at &lt;a href="https://vitejs.dev/guide/why.html" rel="noopener noreferrer"&gt;Why Vite?&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  page load
&lt;/h4&gt;

&lt;p&gt;Next.js loads roughly twice as much client side code when running your app and the client-side router is much more complex. Performance for page load is about 5x with &lt;code&gt;@lazarv/react-server&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  build
&lt;/h4&gt;

&lt;p&gt;When you are finished implementing your app and ready to deploy it to somewhere, you need to build your app for production use. Again, Next.js is lagging behind. Even when using a powerhouse developer machine, it takes seconds to build your Next.js app for production! While the Rollup based build in &lt;code&gt;@lazarv/react-server&lt;/code&gt; is just a blink of an eye compared to the Next.js build. The difference is most prominent in this case between the two frameworks. &lt;code&gt;@lazarv/react-server&lt;/code&gt; wins by a whopping 18x performance gain!&lt;/p&gt;

&lt;h4&gt;
  
  
  start
&lt;/h4&gt;

&lt;p&gt;This is the startup time for the production server of the framework and also the only category which the frameworks are getting a tie.&lt;/p&gt;

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

&lt;p&gt;Do you really need to work with Next.js all the time and in all use case? Surely not. Next.js might be still excellent and awesome for a ton of apps. But there are also way to many apps that are using Next.js even when they should not. By using &lt;code&gt;@lazarv/react-server&lt;/code&gt; developers don't need to use a heavier framework like Next.js when the app is not needing all features or you will run your app on your own. When you would use Vite for a single page application, but might be a bit jealous on the SSR niceties React 19 provides.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>ssr</category>
    </item>
    <item>
      <title>My story creating a new React meta-framework</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Mon, 10 Jun 2024 22:20:51 +0000</pubDate>
      <link>https://dev.to/lazarv/my-story-creating-a-new-react-meta-framework-732</link>
      <guid>https://dev.to/lazarv/my-story-creating-a-new-react-meta-framework-732</guid>
      <description>&lt;p&gt;I have a great idea for a new hobby project! As I am a React developer, I'll use it again for implementing this project, that's an easy choice. I use Next.js at work. It's fine, but there are some pain points in using it, so I don't want to go with it for my own project. I struggle with it during work, so why should I struggle in my spare time. I want to have some fun!&lt;/p&gt;

&lt;p&gt;I love Vite! When I used Vite for a React SPA app, with React Router and react-query, I liked it a lot! It was blazing-fast! No need to wait for any Webpack bundling, just using ES modules, it was heavenly. I started to use Vite when I started experimenting with streaming server-side rendering using React Suspense. I also created a micro-frontend system based on Vite and a self-hosted fork of esm.sh instead of Module Federation, but that's another story.&lt;/p&gt;

&lt;p&gt;But this Vite setup I had for an SPA didn't work for server-side rendering as I wanted it to. Should I use Remix then? Remix now supports Vite! Though choice. I like some parts of Next.js since Next.js 13. More precisely, I like the new React server-side rendering features a lot. React Server Components are awesome! To have complete control over the React hydration and which parts of the rendering is server vs client is great. Using server actions with Next.js is also fantastic! I like how much this new way is close to traditional web development. I like that links and forms are driving navigation, mutation and that state management is shared between the client and the server using standard solutions, like search params and RESTish routing, even some cookies if needed. Web development is again so much easier and fun this way. But I don't want to have all the caching problems of Next.js. I don't want bundles and waiting. So what should I use again? Remix is not supporting these. What about Astro? I like Astro, I would possibly use it for a more content driven project, but this time, it's not my best choice. I only want to use pure React. I heard there's a new minimal React framework, Waku. I want to have even less setup than that!&lt;/p&gt;

&lt;p&gt;To summarize, what will I need or want to use for this project?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;server-side rendering&lt;/li&gt;
&lt;li&gt;React Server Components&lt;/li&gt;
&lt;li&gt;client components&lt;/li&gt;
&lt;li&gt;server actions&lt;/li&gt;
&lt;li&gt;full access to the HTTP context&lt;/li&gt;
&lt;li&gt;a minimal setup, no boilerplate&lt;/li&gt;
&lt;li&gt;fast dev server, build and deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is there a framework like that? No. I worked on some libraries and frameworks in the past, like JayData or odata-v4-server, so if I would create a new React framework with the requirements I have, how should it be done? That's a though question as there's no documentation for these React features yet, just some tests in the React repo and while Next.js is open source, the codebase is huge! I love to experiment with un-documented tech, I loved when I learned the intricacies of the unknown and then tweaking it to the max to create an awesome, jaw-dropping experience, like porting Wolfenstein 3D to the web from scratch, directly porting some C/C++ code to JavaScript while learning a lot or using WebAssembly to create a DOOM web runtime environment to play any WAD files from DOOM I or II, even other DOOM-engine games like Heretic. I loved re-engineering these retro games to make them playable in the browser or on my phone or tablet. I also started to make a dive and started messing around with the Elder Scrolls series to remake it in the browser! It was so much fun! So, without any hesitance, I started to create some proof of concepts.&lt;/p&gt;

&lt;p&gt;RSC is easy. You just need to call &lt;code&gt;renderToReadableStream&lt;/code&gt; and send it to the browser. But what about hydration and client components? Now that's a challenge! You need to render your React tree to the RSC payload in a thread with "react-server" module resolution conditions. This payload will include all the static HTML nodes and references to client components and server actions. To render client components properly, you need to have a snapshot. That's the RSC payload, with the static HTML nodes. Then, you need to get a new React tree from this. To get that, you need to use &lt;code&gt;createFromReadbaleStream&lt;/code&gt; to transform your RSC stream to a React tree, then use a different type of &lt;code&gt;renderToReadableStream&lt;/code&gt;, but this time not using the &lt;em&gt;react-server-dom-webpack&lt;/em&gt; package, but &lt;em&gt;react-dom&lt;/em&gt;. While doing this, you need to send your RSC payload in parallel with the HTML content to hydrate your React application on the client as you stream the server-side rendered content. You also need to have all the references ready to tag client components with the &lt;code&gt;react.client.reference&lt;/code&gt; symbol and if you also want server actions, then with the &lt;code&gt;react.server.reference&lt;/code&gt; symbol. So you need to transform your files differently for multiple environments during rendering and you also need to manage all the module resolution for these. Take this with all the workarounds you need to do to make this work with Vite. So after creating some Vite transform plugins and dealing with the complex rendering (not just a simple &lt;code&gt;renderToString&lt;/code&gt; like in the old days), you're good to go, right?&lt;/p&gt;

&lt;p&gt;A framework is much more, than making some proof of concept work. It's much more, than achieving something in a sandbox. It's very similar to game development or it's very similar to me, to my own game development experience as I always worked on some rendering engine for games and not as a game designer or creating content for a new game (with some tiny exceptions). After you finish with the gameplay, you need to start building all the other parts, like menus, configuration management, etc. A framework is not just a rendering engine and some core functionality. It has to provide some great developer experience. I had this partially because of Vite. If I would not have Vite, my job would have been much harder. But I had an idea. A vision.&lt;/p&gt;

&lt;p&gt;When I experienced node.js the first time, I was amazed. I already worked as a frontend engineer, so node.js for me was a gate to another world, to another dimension. I was able to use all my expertise with JavaScript on the backend. I worked a little bit at this point with other backend solutions, like PHP or ASP.NET. But despite it's quirks, I loved JavaScript (I know, I know...). So node.js was the best thing to continue with. What made node.js awesome? &lt;code&gt;node server.js&lt;/code&gt;. If you had a JavaScript file with any size, be it a tiny script or a large backend for a complex architecture, in the end it's just running a simple &lt;code&gt;node&lt;/code&gt; command to start your server.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;react-server ./App.jsx&lt;/code&gt;. I wanted to have this. So badly! All the frameworks are handling your entrypoint differently. Next.js is enforcing it's own file-based routing. It's so much more opinionated compared to React. Remix has a long setup to follow. I just wanted an &lt;code&gt;App.jsx&lt;/code&gt; and a CLI tool to run it as my entrypoint, taking away all the project setup work that every project needs at the start.&lt;/p&gt;

&lt;p&gt;I imagined this "Get started" (I also love &lt;em&gt;pnpm&lt;/em&gt; btw):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm add @lazarv/react-server
pnpm exec react-server ./App.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need for anything else. Simple as that. Not even a configuration file. Not even installing React directly! Just running the Vite development server, like I would do it for an SPA using &lt;code&gt;npx vite&lt;/code&gt; with an &lt;code&gt;index.html&lt;/code&gt; in place. I was amazed by a Vite demo Evan You did more than 2 years ago. It was minimal. It was elegant. It was awesome! I wanted to have the simplicity of node.js and Vite, just for React.&lt;/p&gt;

&lt;p&gt;I also have to mention a very important presentation. It changed how I look on RSCs and the new way of React server-side rendering. It was Dan Abramov’s RemixConf 2023 presentation titled: “React from Another Dimension”. If you haven’t watched it yet, do it. RSC clicked for me watching it! Also the title was great as I listen to Liquid Tension Experiment a lot! :) &lt;/p&gt;

&lt;p&gt;My hobby project grew out of it’s cooking pot. Maybe it’s some sort of megalomania. But I always lose some scope as I dream big. In all my works, I have a much larger target in the end than at the start of that project. But if I’m committed to that project, I will finish and achieve my vision no matter what.&lt;/p&gt;

&lt;p&gt;If you share the same view on frameworks like me, you can get my experience. Just head over to &lt;a href="https://github.com/lazarv/react-server" rel="noopener noreferrer"&gt;https://github.com/lazarv/react-server&lt;/a&gt; and give as much feedback as possible. I also sort of completed my hobby project. But not the original one. I used my own framework to create the documentation site for it. Check it out at &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;https://react-server.dev&lt;/a&gt;. I hope you will also have fun trying it out!&lt;/p&gt;

</description>
      <category>react</category>
      <category>vite</category>
      <category>rsc</category>
    </item>
    <item>
      <title>Todo app with no client-side JavaScript using @lazarv/react-server</title>
      <dc:creator>Viktor Lázár</dc:creator>
      <pubDate>Mon, 27 May 2024 17:02:41 +0000</pubDate>
      <link>https://dev.to/lazarv/todo-app-with-no-client-side-javascript-using-lazarvreact-server-23ig</link>
      <guid>https://dev.to/lazarv/todo-app-with-no-client-side-javascript-using-lazarvreact-server-23ig</guid>
      <description>&lt;p&gt;Everyone starts with a simple Todo app when evaluating a new framework. So let’s do it this time using &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;@lazarv/react-server&lt;/a&gt;, a new minimalist React meta-framework using Vite!&lt;/p&gt;

&lt;p&gt;Our goal with this example is to use no client-side JavaScript and no React hydration. We only want to use React Server Components and Server Actions. Is this possible? Absolutely!&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;Let’s create a new project, by creating a new folder, initializing pnpm and installing all the required dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;todo
&lt;span class="nb"&gt;cd &lt;/span&gt;todo
pnpm init
pnpm config &lt;span class="nb"&gt;set &lt;/span&gt;auto-install-peers &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; project
pnpm add @lazarv/react-server better-sqlite3 zod
pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; @types/better-sqlite3 @types/react @types/react-dom autoprefixer postcss tailwindcss typescript
pnpx tailwindcss init &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To store our Todo items, we will use a local Sqlite database. For validation, we will use Zod and for styling we will use Tailwind CSS. To include all our source code as Tailwind content, change the &lt;strong&gt;tailwind.config.js&lt;/strong&gt; to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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;plugins&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;As we will not do any exciting Tailwind styling, put the usual 3-liner Tailwind setup into &lt;strong&gt;src/index.css&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hello World!
&lt;/h2&gt;

&lt;p&gt;Well it’s nothing better than a good old “Hello World!” app, so let’s create an entrypoint for our Todo app! Put the following code into &lt;strong&gt;src/index.tsx&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World!&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;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run this micro-app, you can just use &lt;code&gt;pnpm exec react-server ./src/index.tsx&lt;/code&gt; . To make our life easier, let’s add some npm scripts to &lt;strong&gt;package.json&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server ./src/index.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server build ./src/index.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-server start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After doing this, use &lt;code&gt;pnpm dev&lt;/code&gt; to start the development server. After our development server is running, open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and be proud of our Hello World! app. You can also use &lt;code&gt;pnpm dev --open&lt;/code&gt; to do so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout
&lt;/h2&gt;

&lt;p&gt;Our Todo app needs a layout, so let’s create &lt;strong&gt;src/Layout.tsx&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&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="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&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;head&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;meta&lt;/span&gt; &lt;span class="na"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&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;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&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;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Todo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&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;head&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;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"p-4"&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="s"&gt;"text-4xl font-bold mb-4"&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;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Todo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&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="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;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;body&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;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing fancy here, just a usual HTML document template. We will use this Layout component as a wrapper for our app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page
&lt;/h2&gt;

&lt;p&gt;Our main page will be the Todo app, where we will use all of the building blocks to create our app. Let’s say goodbye to Hello World! and change the component to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;allTodos&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;./actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AddTodo&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;./AddTodo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Item&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;./Item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Layout&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;./Layout&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Index&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;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;allTodos&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;Layout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AddTodo&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;todos&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="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-500"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;No todos yet!&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&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;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Item&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;todo&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&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;))&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;Layout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this React Server Component, we collect all the stored Todo items by calling &lt;code&gt;allTodos()&lt;/code&gt; and use the result to render JSX. We use the Layout component to wrap our content into a HTML document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Item
&lt;/h2&gt;

&lt;p&gt;To render our items, let’s create an Item component in &lt;strong&gt;src/Item.tsx&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&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;./actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&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="nx"&gt;number&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="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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&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;title&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex row items-center justify-between py-1 px-4 my-1 rounded-lg text-lg border bg-gray-100 text-gray-600 mb-2"&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1"&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;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;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;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;deleteTodo&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;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-medium"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Item component will render our Todo item using an &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt; prop. But what about that &lt;code&gt;&amp;lt;form action={deleteTodo}&amp;gt;&lt;/code&gt;? It’s a server action! When the user will submit the form by clicking on the “Delete” button, the browser will call our server action. This is possible without any JavaScript on the frontend, as React supports progressive enhancement for server actions and the initial form action will call the server action by including a hidden input field in the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"$ACTION_ID_/Users/lazarv/Projects/tutorials/todo/src/actions.ts#deleteTodo"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The framework will resolve this &lt;code&gt;$ACTION_ID_&lt;/code&gt; prefixed path to the server action and it will call our server action function!&lt;/p&gt;

&lt;h2&gt;
  
  
  Server actions
&lt;/h2&gt;

&lt;p&gt;We will use server actions to implement all functionality of our Todo app. This is the most complex part of the app, but don’t shy away, it’s still really very simple, let’s create &lt;strong&gt;src/actions.ts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;redirect&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;@lazarv/react-server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Database&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;better-sqlite3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;zod&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;zod&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;db&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;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.sqlite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&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="nx"&gt;number&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="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTodoSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zod&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title must be at least 3 characters&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="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title must be at most 100 characters&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="nf"&gt;refine&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&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;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title is required&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="nf"&gt;transform&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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;deleteTodoSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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="nx"&gt;zod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;transform&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseInt&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;10&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;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addTodoSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&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="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO todos(title) VALUES (?)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;allTodos&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&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="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;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;deleteTodoSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&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="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE FROM todos WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&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="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first line of this file, we instrument the framework to treat this file as a server action module using the &lt;code&gt;“use server”;&lt;/code&gt; directive. All exported async functions will be available for us to use as server actions.&lt;/p&gt;

&lt;p&gt;We initialize the Sqlite database on module import and create Zod schemas for item add and delete operations.&lt;/p&gt;

&lt;p&gt;In all server actions, you will receive a &lt;code&gt;FormData&lt;/code&gt; instance, including all the fields we define in the forms. We &lt;code&gt;safeParse&lt;/code&gt; these after converting to JavaScript objects using &lt;code&gt;Object.fromEntries&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If Zod validation fails, we throw the validation issues as an error.&lt;/p&gt;

&lt;p&gt;On success, we run a database command to INSERT or DELETE the Todo item.&lt;/p&gt;

&lt;p&gt;At the end, we are using &lt;code&gt;redirect&lt;/code&gt; to navigate the user back from the server action call. This is needed as we don’t want the user to use a browser page refresh to create or delete the Todo item again, reusing the form submit.&lt;/p&gt;

&lt;p&gt;We also implemented the &lt;code&gt;allTodos&lt;/code&gt; function here, to have all the storage related code in a single file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add new item
&lt;/h2&gt;

&lt;p&gt;To implement the AddTodo component, create an &lt;strong&gt;src/AddTodo.tsx&lt;/strong&gt; file with the following content:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useActionState&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;@lazarv/react-server/router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ZodIssue&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;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addTodo&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;./actions&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AddTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useActionState&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;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ZodIssue&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;addTodo&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;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-2"&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;input&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-50 border border-gray-300 text-gray-900 rounded-lg p-2.5"&lt;/span&gt;
          &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&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="nx"&gt;string&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;autoFocus&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;button&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-white bg-blue-700 hover:bg-blue-800 rounded-lg px-5 py-2 mb-2 text-center"&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Submit
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;?.(({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;i&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;p&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-red-50 border rounded-lg border-red-500 text-red-500 p-2.5 mb-2"&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;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-red-50 border rounded-lg border-red-500 text-red-500 p-2.5"&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;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We already know, how to use server actions from a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;. But what about the result of our server action call? &lt;code&gt;useActionState&lt;/code&gt; to the rescue!&lt;/p&gt;

&lt;p&gt;By passing the &lt;code&gt;addTodo&lt;/code&gt; server action function reference to &lt;code&gt;useActionState&lt;/code&gt; , we can get the result of the server action call when this specific server action was called, so we can collect the &lt;code&gt;error&lt;/code&gt; result. This will be the Zod error issues we thrown in the add server action. So we can iterate on all Zod validation issues here and render validation error messages on the server side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production build
&lt;/h2&gt;

&lt;p&gt;During using the development server, you can notice that the page loaded some JavaScript modules in the browser. This is only used for Hot Module Replacement. In a production build, only the document and a CSS asset will be loaded in the browser.&lt;/p&gt;

&lt;p&gt;To build for production, run &lt;code&gt;pnpm build&lt;/code&gt; and then you can start the production server using &lt;code&gt;pnpm start&lt;/code&gt; . &lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;That’s it! We have a working small Todo example. You can also find the example on &lt;a href="https://github.com/lazarv/react-server/tree/main/examples/todo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. By using the above approach, it will be easy for you to add a Todo item update feature, marking an item as completed. I hope you had fun and you will like the developer experience the &lt;a href="https://react-server.dev" rel="noopener noreferrer"&gt;@lazarv/react-server&lt;/a&gt; framework provides! There are a lot of other exciting features provided by React and the framework itself! We don’t used any client components here, that will be in another tutorial. The documentation site of &lt;code&gt;@lazarv/react-server&lt;/code&gt; was created using the framework and it’s build and deployment to Vercel takes about 5s! Because the framework uses Vite, the developer experience is blazing-fast!&lt;/p&gt;

</description>
      <category>react</category>
      <category>ssr</category>
      <category>reactserver</category>
      <category>vite</category>
    </item>
  </channel>
</rss>
