<?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: Tophe</title>
    <description>The latest articles on DEV Community by Tophe (@topheman).</description>
    <link>https://dev.to/topheman</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%2F29953%2Fd3ca8e20-5c7e-44f3-ace3-40e7e6fe7eba.jpeg</url>
      <title>DEV Community: Tophe</title>
      <link>https://dev.to/topheman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/topheman"/>
    <language>en</language>
    <item>
      <title>Writing components in TypeScript with jco - WebAssembly Component Model</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Fri, 19 Sep 2025 07:33:20 +0000</pubDate>
      <link>https://dev.to/topheman/webassembly-component-model-writing-components-in-typescript-with-jco-183j</link>
      <guid>https://dev.to/topheman/webassembly-component-model-writing-components-in-typescript-with-jco-183j</guid>
      <description>&lt;p&gt;This part isn’t required to use the project — but if you want to experiment with writing plugins in TypeScript, here’s how it works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The downsides of writing plugins in TypeScript is mostly that your &lt;code&gt;.wasm&lt;/code&gt; file will be &lt;strong&gt;much larger&lt;/strong&gt; than the one compiled from rust or C:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~100KB of wasm for the rust plugin&lt;/li&gt;
&lt;li&gt;~12MB of wasm for the TypeScript plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason is that a JavaScript runtime needs to be embedded in the &lt;code&gt;.wasm&lt;/code&gt; file, which is not the case for the rust plugin.&lt;/p&gt;

&lt;p&gt;More about the &lt;a href="https://github.com/bytecodealliance/ComponentizeJS?tab=readme-ov-file#explainer" rel="noopener noreferrer"&gt;SpiderMonkey runtime embedding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is setup in the project, however, I don't rely on plugins written in TypeScript for the moment as would be too much of a performance hit (mostly for the web host).&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;plugin&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pluginApi&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;./types/plugin-api&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;const&lt;/span&gt; &lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;pluginApi&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;echo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;man&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;payload&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="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;a href="https://github.com/bytecodealliance/jco" rel="noopener noreferrer"&gt;&lt;code&gt;jco&lt;/code&gt;&lt;/a&gt; toolchain helps compile JS/TS code into WebAssembly components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Generate TypeScript types:
&lt;/li&gt;
&lt;/ol&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;packages/plugin-name
jco types &lt;span class="nt"&gt;--world-name&lt;/span&gt; plugin-api &lt;span class="nt"&gt;--out-dir&lt;/span&gt; ./src/types ../../crates/pluginlab/wit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Bundle your TS code into a single file (using &lt;code&gt;rolldown&lt;/code&gt; or another bundler).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rolldown ./src/component.ts &lt;span class="nt"&gt;--file&lt;/span&gt; ./dist/component.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Componentize it:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jco componentize ./dist/component.js &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wit&lt;/span&gt; ../../crates/pluginlab/wit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--world-name&lt;/span&gt; plugin-api &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; ./dist/component.wasm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--disable&lt;/span&gt; http &lt;span class="nt"&gt;--disable&lt;/span&gt; random
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;(Optional) Optimize the resulting &lt;code&gt;.wasm&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jco opt ./dist/component.wasm &lt;span class="nt"&gt;-o&lt;/span&gt; ./dist/component-opt.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example: &lt;code&gt;package.json&lt;/code&gt; Scripts
&lt;/h2&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;"wit-types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jco types ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bundle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rolldown ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"componentize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jco componentize ..."&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;"npm run wit-types &amp;amp;&amp;amp; npm run bundle &amp;amp;&amp;amp; npm run componentize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"optimize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jco opt ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typecheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc --noEmit"&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;📎 Here are links to the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/packages/plugin-echo/src/component.ts" rel="noopener noreferrer"&gt;implementation&lt;/a&gt; of the &lt;code&gt;plugin-echo&lt;/code&gt; plugin in TypeScript in the project.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>wasi</category>
      <category>plugin</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Writing components in Go with TinyGo compiler - WebAssembly Component Model</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Fri, 19 Sep 2025 07:32:56 +0000</pubDate>
      <link>https://dev.to/topheman/webassembly-component-model-writing-components-in-go-with-tinygo-compiler-2914</link>
      <guid>https://dev.to/topheman/webassembly-component-model-writing-components-in-go-with-tinygo-compiler-2914</guid>
      <description>&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Required tools for TinyGo WebAssembly Component Model support:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TinyGo compiler&lt;/strong&gt; v0.39.0+: Compiles Go code to WebAssembly components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;wkg&lt;/code&gt;&lt;/strong&gt;: Resolves and bundles WIT dependencies into a single package&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;wasm-tools&lt;/code&gt;&lt;/strong&gt;: Converts WebAssembly modules to components&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build Process
&lt;/h2&gt;

&lt;p&gt;Go plugins require a specific build process due to TinyGo's Component Model requirements:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Prepare WIT files for TinyGo
&lt;/h3&gt;

&lt;p&gt;TinyGo has specific requirements that differ from other languages. The &lt;code&gt;prepare-wit-files.sh&lt;/code&gt; script (a custom script for this project) copies WIT files from &lt;code&gt;crates/pluginlab/wit&lt;/code&gt; to &lt;code&gt;go_modules/wit&lt;/code&gt; and uncomments TinyGo-specific lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/prepare-wit-files.sh &lt;span class="nt"&gt;-i&lt;/span&gt; crates/pluginlab/wit &lt;span class="nt"&gt;-o&lt;/span&gt; go_modules/wit &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"SPECIFIC TinyGo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this workflow exists:&lt;/strong&gt; The &lt;code&gt;go_modules/wit&lt;/code&gt; directory is gitignored and contains modified versions of the WIT files specifically for TinyGo. This workflow maintains &lt;code&gt;crates/pluginlab/wit&lt;/code&gt; as the single source of truth for all WIT definitions, while automatically generating TinyGo-compatible versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this is needed:&lt;/strong&gt; The &lt;code&gt;wasip2&lt;/code&gt; target of TinyGo assumes that component is targeting &lt;code&gt;wasi:cli/command@0.2.0&lt;/code&gt; and requires explicit inclusion of WASI imports. The script searches for lines containing "SPECIFIC TinyGo" and uncomments them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;world plugin-api {
&lt;/span&gt;  // The wasip2 target of TinyGo assumes that the component is targeting
  // wasi:cli/command@0.2.0 world (part of wasi:cli), so it needs to
  // include the imports from that world.
  // It's only included for the versions of the wit files destined for TinyGo.
&lt;span class="gd"&gt;- // include wasi:cli/imports@0.2.0; // SPECIFIC TinyGo - DO NOT CHANGE THIS LINE
&lt;/span&gt;&lt;span class="gi"&gt;+ include wasi:cli/imports@0.2.0; // SPECIFIC TinyGo - DO NOT CHANGE THIS LINE
&lt;/span&gt;  import http-client;
  import host-state-plugin;
  export plugin;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Bundle WIT dependencies with wkg
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why wkg is used:&lt;/strong&gt; TinyGo cannot resolve WIT imports on its own - it needs all dependencies to be either bundled into a single WASM file or pre-resolved into a local directory. For example, when TinyGo sees &lt;code&gt;include wasi:cli/imports@0.2.0;&lt;/code&gt;, it doesn't know where to find that external package or what's inside it. The &lt;code&gt;wkg&lt;/code&gt; tool handles dependency resolution by fetching all imported WIT interfaces and creating a complete WIT package:&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;go_modules/
wkg wit build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates &lt;code&gt;repl:api.wasm&lt;/code&gt; containing all the WIT definitions needed by the plugin. The command also generates a &lt;code&gt;wkg.lock&lt;/code&gt; file that locks the dependency versions for reproducible builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Generate Go bindings
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;wit-bindgen-go&lt;/code&gt; to generate Go code from the bundled WIT package:&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;go_modules/plugin-name/
go tool wit-bindgen-go generate &lt;span class="nt"&gt;--world&lt;/span&gt; plugin-api &lt;span class="nt"&gt;--out&lt;/span&gt; internal ../repl:api.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates the &lt;code&gt;internal/&lt;/code&gt; directory with all the generated Go bindings for the plugin interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Compile with TinyGo
&lt;/h3&gt;

&lt;p&gt;TinyGo compiles the Go code directly to a WebAssembly component:&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;go_modules/plugin-name/
tinygo build &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasip2 &lt;span class="nt"&gt;--wit-package&lt;/span&gt; ../repl:api.wasm &lt;span class="nt"&gt;--wit-world&lt;/span&gt; plugin-api &lt;span class="nt"&gt;-o&lt;/span&gt; plugin-name-go.wasm main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  File Structure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Project organization:&lt;/strong&gt; Go plugins follow this structure in the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go_modules/
  wit/                           # Modified WIT files for TinyGo
    plugin-api.wit               # Contains TinyGo-specific includes
    shared.wit                   # Shared types and interfaces
    host-api.wit                 # Host API definitions
  repl:api.wasm                  # Bundled WIT package (from wkg)
  wkg.lock                       # Lock file for WIT dependencies (from wkg)
  plugin-echo/                   # Plugin directory
    go.mod                       # Go module with wit-bindgen-go tool
    main.go                      # Plugin implementation
    internal/                    # Generated bindings (from wit-bindgen-go)
      repl/api/plugin/           # Plugin interface bindings
      repl/api/transport/        # Transport type bindings
      wasi/                      # WASI interface bindings
    plugin-echo-go.wasm          # Final WebAssembly component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Plugin Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Standard pattern:&lt;/strong&gt; Go plugins use the &lt;code&gt;init()&lt;/code&gt; function to register their exports, following the WebAssembly Component Model pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"webassembly-repl/plugin-echo/internal/repl/api/plugin"&lt;/span&gt;
    &lt;span class="s"&gt;"webassembly-repl/plugin-echo/internal/repl/api/transport"&lt;/span&gt;
    &lt;span class="s"&gt;"go.bytecodealliance.org/cm"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exports&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"echogo"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exports&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Man&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;`... some man text ...`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exports&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReplStatusSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&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;return&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}]](&lt;/span&gt;&lt;span class="n"&gt;response&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="c"&gt;// main is required for the wasip2 target&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Go Module Configuration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Required setup:&lt;/strong&gt; The &lt;code&gt;go.mod&lt;/code&gt; file must include the &lt;code&gt;wit-bindgen-go&lt;/code&gt; tool as a Go tool dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;webassembly&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;repl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="m"&gt;1.24&lt;/span&gt;

&lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bytecodealliance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bindgen&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;

&lt;span class="c"&gt;// ... other dependencies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the project to use &lt;code&gt;go tool wit-bindgen-go&lt;/code&gt; commands for generating bindings.&lt;/p&gt;

&lt;p&gt;📎 Here are links to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/tree/master/go_modules" rel="noopener noreferrer"&gt;go_modules&lt;/a&gt; directory that contains the Go plugins&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/justfile" rel="noopener noreferrer"&gt;justfile&lt;/a&gt; file that contains the build commands for Go plugins&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/16" rel="noopener noreferrer"&gt;PR#16 - Add Go Language Plugin Support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webassembly</category>
      <category>wasi</category>
      <category>go</category>
      <category>tinygo</category>
    </item>
    <item>
      <title>Writing components in C with WASI SDK - WebAssembly Component Model</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Fri, 19 Sep 2025 07:29:50 +0000</pubDate>
      <link>https://dev.to/topheman/webassembly-component-model-writing-components-in-c-with-wasi-sdk-5b5o</link>
      <guid>https://dev.to/topheman/webassembly-component-model-writing-components-in-c-with-wasi-sdk-5b5o</guid>
      <description>&lt;h2&gt;
  
  
  What is WASI-SDK?
&lt;/h2&gt;

&lt;p&gt;Unlike Rust with &lt;code&gt;cargo-component&lt;/code&gt;, C/C++ lacks an integrated toolchain for building WebAssembly components. The &lt;a href="https://github.com/WebAssembly/wasi-sdk" rel="noopener noreferrer"&gt;WASI SDK&lt;/a&gt; provides the essential tooling needed to compile C code to WebAssembly.&lt;/p&gt;

&lt;p&gt;The WASI SDK includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;clang&lt;/code&gt; compiler&lt;/strong&gt; configured with a WASI sysroot (complete set of target platform headers and libraries) for the &lt;code&gt;wasm32-wasi&lt;/code&gt; target&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WASI-enabled C standard library&lt;/strong&gt; (&lt;code&gt;libc&lt;/code&gt;) that implements WASI interfaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform support&lt;/strong&gt; for different operating systems and architectures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview 2 compatibility&lt;/strong&gt; for building modern WebAssembly components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows you to write C plugins that can access filesystem, networking, and other system resources through WASI interfaces, just like Rust plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  WASI-SDK Setup
&lt;/h2&gt;

&lt;p&gt;The project uses a custom script &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/justfile" rel="noopener noreferrer"&gt;&lt;code&gt;just dl-wasi-sdk&lt;/code&gt;&lt;/a&gt; that acts like a package manager, automatically downloading and extracting the correct version of the WASI SDK for your OS/architecture into &lt;code&gt;c_deps/&lt;/code&gt; (which acts like a &lt;code&gt;node_modules&lt;/code&gt; for C dependencies).&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write a C plugin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build Process
&lt;/h3&gt;

&lt;p&gt;C plugins are built using a two-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generate bindings&lt;/strong&gt;: &lt;code&gt;wit-bindgen c ./crates/pluginlab/wit --world plugin-api --out-dir ./c_modules/plugin-name&lt;/code&gt; creates the C bindings from your WIT interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile and convert&lt;/strong&gt;: Use the WASI SDK's &lt;code&gt;clang&lt;/code&gt; to compile C code to a WebAssembly module (P1), then convert it to a P2 component&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The build process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiles &lt;code&gt;component.c&lt;/code&gt;, &lt;code&gt;plugin_api.c&lt;/code&gt;, and &lt;code&gt;plugin_api_component_type.o&lt;/code&gt; to a WebAssembly module with &lt;code&gt;-mexec-model=reactor&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  ./c_deps/wasi-sdk/bin/clang component.c plugin_api.c plugin_api_component_type.o &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; plugin-name-c.module.p1.wasm &lt;span class="nt"&gt;-mexec-model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;reactor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Converts the P1 module to a P2 component using &lt;code&gt;wasm-tools component new&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  wasm-tools component new plugin-name-c.module.p1.wasm &lt;span class="nt"&gt;-o&lt;/span&gt; plugin-name-c.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  File Structure
&lt;/h3&gt;

&lt;p&gt;The C plugins follow this structure in the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c_deps/                           # WASI SDK installation
c_modules/
  plugin-echo/                    # Plugin directory
    component.c                   # Your plugin implementation
    plugin_api.c                  # Generated bindings (from wit-bindgen)
    plugin_api.h                  # Generated header (from wit-bindgen)
    plugin_api_component_type.o   # Generated object file (from wit-bindgen)
    plugin-echo-c.module.p1.wasm  # Compiled WebAssembly module (P1)
    plugin-echo-c.wasm            # Final WebAssembly component (P2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Plugin Implementation
&lt;/h2&gt;

&lt;p&gt;The C plugin implements the same interface as the Rust version, with function signatures generated from the WIT interface by &lt;code&gt;wit-bindgen&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;exports_repl_api_plugin_name()&lt;/code&gt;&lt;/strong&gt; corresponds to &lt;code&gt;fn name() -&amp;gt; String&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;exports_repl_api_plugin_man()&lt;/code&gt;&lt;/strong&gt; corresponds to &lt;code&gt;fn man() -&amp;gt; String&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;exports_repl_api_plugin_run()&lt;/code&gt;&lt;/strong&gt; corresponds to &lt;code&gt;fn run(payload: String) -&amp;gt; Result&amp;lt;PluginResponse, ()&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the key implementation details - &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/c_modules/plugin-echo/component.c" rel="noopener noreferrer"&gt;&lt;code&gt;plugin-echo/component.c&lt;/code&gt;&lt;/a&gt;:&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;#include&lt;/span&gt; &lt;span class="cpf"&gt;"plugin_api.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;exports_repl_api_plugin_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin_api_string_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Populate ret with "echoc" as the plugin name&lt;/span&gt;
    &lt;span class="c1"&gt;// plugin_api_string_dup() allocates new memory and copies the string&lt;/span&gt;
    &lt;span class="n"&gt;plugin_api_string_dup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"echoc"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;exports_repl_api_plugin_man&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin_api_string_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Populate ret with the manual text for the echo command&lt;/span&gt;
    &lt;span class="c1"&gt;// plugin_api_string_dup() allocates new memory and copies the string&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;man_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"some man text ...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;plugin_api_string_dup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;man_text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;exports_repl_api_plugin_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin_api_string_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exports_repl_api_plugin_plugin_response_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Set status to success (0 = success, 1 = error)&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;REPL_API_TRANSPORT_REPL_STATUS_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Set stdout to contain the payload&lt;/span&gt;
    &lt;span class="c1"&gt;// is_some = true means the optional string has a value&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a properly null-terminated string from the payload&lt;/span&gt;
    &lt;span class="c1"&gt;// The payload has ptr and len, we need to ensure it's null-terminated&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;temp_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;len&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_str&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle allocation failure&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Copy the payload data and null-terminate it&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;temp_str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'\0'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Use plugin_api_string_dup to create the output string&lt;/span&gt;
    &lt;span class="n"&gt;plugin_api_string_dup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Free our temporary string&lt;/span&gt;
    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Set stderr to none (no error output)&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Return true for success (false would indicate an error)&lt;/span&gt;
    &lt;span class="c1"&gt;// This corresponds to Ok(response) in the Rust Result&amp;lt;T, ()&amp;gt; pattern&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&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;&lt;strong&gt;Memory Management Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input parameters (like &lt;code&gt;payload&lt;/code&gt;) are owned by the runtime - they MUST NOT be freed by the plugin&lt;/li&gt;
&lt;li&gt;Output parameters (like &lt;code&gt;ret&lt;/code&gt;) are populated by the plugin, freed by the runtime&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugin_api_string_dup()&lt;/code&gt; allocates new memory for string copies&lt;/li&gt;
&lt;li&gt;The generated &lt;code&gt;_free&lt;/code&gt; functions handle cleanup automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Differences from Rust:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual memory management for temporary strings&lt;/li&gt;
&lt;li&gt;Explicit handling of string length vs null termination&lt;/li&gt;
&lt;li&gt;Boolean return values instead of Rust's &lt;code&gt;Result&amp;lt;T, ()&amp;gt;&lt;/code&gt; pattern&lt;/li&gt;
&lt;li&gt;Direct manipulation of the generated C structs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📎 Here are links to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/c_modules/plugin-echo/component.c" rel="noopener noreferrer"&gt;plugin-echo&lt;/a&gt; C implementation and the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/c_modules/plugin-echo/plugin_api.h" rel="noopener noreferrer"&gt;plugin_api.h&lt;/a&gt; header file&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/6" rel="noopener noreferrer"&gt;PR#6 - Add C Language Plugin Support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webassembly</category>
      <category>wasi</category>
      <category>plugin</category>
      <category>c</category>
    </item>
    <item>
      <title>Building components in multiple languages - WebAssembly Component Model</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Fri, 19 Sep 2025 07:29:10 +0000</pubDate>
      <link>https://dev.to/topheman/webassembly-component-model-building-components-in-multiple-languages-27kg</link>
      <guid>https://dev.to/topheman/webassembly-component-model-building-components-in-multiple-languages-27kg</guid>
      <description>&lt;p&gt;The Bytecode Alliance organization maintains a very useful website about &lt;a href="https://bytecodealliance.org/articles/the-webassembly-component-model" rel="noopener noreferrer"&gt;The WebAssembly Component Model&lt;/a&gt; and each associated languages has also a page on it.&lt;/p&gt;

&lt;p&gt;Now that I've explained the purpose of the project and its architecture in the previous section, I will focus on concrete examples of implementing multiple languages in the same project and sharing some tips I had to learn the hard way.&lt;/p&gt;

&lt;p&gt;Before choosing a language for compiling to WebAssembly, you should consider the impact it has on the final size of your wasm file - here is a comparaison of the size of the &lt;code&gt;echo&lt;/code&gt; plugin in the different languages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;56K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;72K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go (with TinyGo)&lt;/td&gt;
&lt;td&gt;332K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Looking at the table above, the differences in &lt;code&gt;.wasm&lt;/code&gt; file size come from the language and how much runtime support it brings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C and Rust&lt;/strong&gt; produce very small files (56K and 72K here) because they compile directly to efficient machine-level code without needing to ship a runtime. Rust is slightly larger due to safety checks and standard library overhead, but both remain compact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go (TinyGo)&lt;/strong&gt; is larger (332K) because Go normally includes a large runtime. TinyGo reduces this compared to regular Go, but you still pay a higher size cost than C/Rust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; is much bigger (12M) because it doesn’t compile directly to Wasm. Instead, you’re shipping a whole JavaScript engine runtime inside your &lt;code&gt;.wasm&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've already covered creating Rust components, let's now focus on C, Go and TypeScript.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>wasi</category>
      <category>plugin</category>
      <category>rust</category>
    </item>
    <item>
      <title>Building a plugin system - WebAssembly Component Model</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Fri, 19 Sep 2025 07:28:55 +0000</pubDate>
      <link>https://dev.to/topheman/webassembly-component-model-building-a-plugin-system-58o0</link>
      <guid>https://dev.to/topheman/webassembly-component-model-building-a-plugin-system-58o0</guid>
      <description>&lt;p&gt;The WebAssembly Component Model (WCM) is still very early days, but it's a very promising technology. However, the examples out there are either &lt;strong&gt;too simple&lt;/strong&gt; or &lt;strong&gt;too complex&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal of my project &lt;a href="https://github.com/topheman/webassembly-component-model-experiments" rel="noopener noreferrer"&gt;topheman/webassembly-component-model-experiments&lt;/a&gt; is to demonstrate the power of WCM — with more than just a simple "Hello, World".&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;This project is an experiment to explore and understand the &lt;strong&gt;WebAssembly Component Model&lt;/strong&gt; through a concrete application: a modular REPL where each command is implemented as a Wasm component.&lt;/p&gt;

&lt;p&gt;The goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a real-world project using WebAssembly Component Model.&lt;/li&gt;
&lt;li&gt;Evaluate DX, tooling, portability, and interoperability.&lt;/li&gt;
&lt;li&gt;Use WIT files to define interfaces and enforce isolation/sandboxing.&lt;/li&gt;
&lt;li&gt;Run the &lt;strong&gt;same plugin code&lt;/strong&gt; (examples in Rust, C, Go and TypeScript) across &lt;strong&gt;CLI&lt;/strong&gt; (Rust) and &lt;strong&gt;browser&lt;/strong&gt; (TypeScript) hosts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That way, it would demonstrate features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You could run a third-party plugin that is written in a different language&lt;/li&gt;
&lt;li&gt;Without having to trust the author, since the plugins are sandboxed and isolated from the host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll first go over the basics of WebAssembly Component Model, then dig into the project and its architecture — so you get a more hands-on understanding of the technology through an actual use case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://topheman.github.io/webassembly-component-model-experiments/" rel="noopener noreferrer"&gt;🧪 Try the online demo (web version)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;▶️ Example of running the CLI &lt;code&gt;pluginlab&lt;/code&gt; 👇&lt;/p&gt;

&lt;center&gt;
  &lt;a href="https://asciinema.org/a/DWYAgrjSpwlejvRJQY8AHCEfD?speed=1.5" rel="noopener noreferrer"&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftopheman%2Fwebassembly-component-model-experiments%2Fblob%2Fmaster%2Fcrates%2Fpluginlab%2Fdemo-preview-opt.png%3Fraw%3Dtrue" alt="pluginlab demo" width="700" height="499"&gt;
  &lt;/a&gt;
&lt;/center&gt;

&lt;h2&gt;
  
  
  2. From WebAssembly MVP to Component Model
&lt;/h2&gt;

&lt;p&gt;WebAssembly has evolved over time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebAssembly → WASI → WebAssembly Component Model&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  WebAssembly MVP (2017)
&lt;/h3&gt;

&lt;p&gt;WebAssembly began as a low-level binary format — portable, secure and fast — but everything had to be wired manually when dealing with multiple modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  WASI (WebAssembly System Interface)
&lt;/h3&gt;

&lt;p&gt;WASI added a way for Wasm modules to interact with the host (files, networking, etc.) securely. It has evolved through previews:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preview 1&lt;/strong&gt; (deprecated): Initial WASI specification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview 2&lt;/strong&gt; (current): Stable APIs used in this project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview 3&lt;/strong&gt; (future): Enhanced capabilities like async operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But WASI didn't really solve how to &lt;em&gt;compose&lt;/em&gt; multiple modules easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebAssembly Component Model
&lt;/h3&gt;

&lt;p&gt;That’s where WCM comes in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WIT (WebAssembly Interface Types)&lt;/strong&gt;: define interfaces with strong typing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Componentization&lt;/strong&gt;: encapsulate logic into reusable, sandboxed units.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typed imports/exports&lt;/strong&gt;: clean separation of responsibilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It makes it much easier to build systems from Wasm components — like plugins — without glue code everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Defining Interfaces with WIT
&lt;/h2&gt;

&lt;p&gt;WIT (WebAssembly Interface Types) is an IDL (Interface Description Language). It defines the contract between components and host.&lt;/p&gt;

&lt;p&gt;Here’s a simplified version of the &lt;code&gt;plugin-api.wit&lt;/code&gt; file used in &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/tree/master/crates/pluginlab/wit" rel="noopener noreferrer"&gt;this project&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package repl:api;

interface plugin {
  enum repl-status { success, error }

  record plugin-response {
    status: repl-status,
    stdout: option&amp;lt;string&amp;gt;,
    stderr: option&amp;lt;string&amp;gt;,
  }

  name: func() -&amp;gt; string;
  man: func() -&amp;gt; string;
  run: func(payload: string) -&amp;gt; result&amp;lt;plugin-response&amp;gt;;
}

interface http-client {
  record http-header { name: string, value: string }

  record http-response {
    status: u16,
    ok: bool,
    headers: list&amp;lt;http-header&amp;gt;,
    body: string,
  }

  get: func(url: string, headers: list&amp;lt;http-header&amp;gt;) -&amp;gt; result&amp;lt;http-response, string&amp;gt;;
}

world plugin-api {
  import http-client;
  export plugin;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A WIT &lt;a href="https://component-model.bytecodealliance.org/design/worlds.html" rel="noopener noreferrer"&gt;&lt;code&gt;world&lt;/code&gt;&lt;/a&gt; is a higher-level contract that describes a component's capabilities and needs.&lt;/p&gt;

&lt;p&gt;The world &lt;code&gt;plugin-api&lt;/code&gt; enforces the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;plugin&lt;/strong&gt; (Wasm Component) implements the &lt;code&gt;plugin&lt;/code&gt; interface.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;host&lt;/strong&gt; provides the &lt;code&gt;http-client&lt;/code&gt; interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation lets plugins remain unaware of the host environment (CLI or browser).&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Creating WebAssembly Components in Rust
&lt;/h2&gt;

&lt;p&gt;For C, Go and TypeScript, see the following posts.&lt;/p&gt;

&lt;p&gt;With Rust, you use &lt;code&gt;cargo-component&lt;/code&gt; (which relies on &lt;code&gt;wit-bindgen&lt;/code&gt;) to generate bindings from your WIT definitions. Here’s a stripped-down version of the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/crates/plugin-echo/src/lib.rs" rel="noopener noreferrer"&gt;echo plugin&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;repl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Guest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReplStatus&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Guest&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="nf"&gt;.to_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;fn&lt;/span&gt; &lt;span class="nf"&gt;man&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Some man page"&lt;/span&gt;&lt;span class="nf"&gt;.to_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;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PluginResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;ReplStatus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&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="nn"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;export!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt; &lt;span class="n"&gt;with_types_in&lt;/span&gt; &lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to do networking or filesystem access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;networking: you will use the &lt;code&gt;http-client&lt;/code&gt; interface, the host will provide its own implementation&lt;/li&gt;
&lt;li&gt;filesystem: you will use the standard library of your language, like &lt;code&gt;std::fs::*&lt;/code&gt; in Rust or &lt;code&gt;fs.readFileSync()&lt;/code&gt; in TypeScript - the host will provide transparent bindings to the host's filesystem&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build process
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo component build &lt;span class="nt"&gt;-p&lt;/span&gt; plugin-echo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a &lt;code&gt;target/wasm32-wasip1/debug/plugin_echo.wasm&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Hosts: CLI and Browser
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔧 CLI Host (Rust)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kz841d2z3xtbflxuqfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kz841d2z3xtbflxuqfp.png" alt="Architecture Cli" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CLI host (&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/tree/master/crates/pluginlab" rel="noopener noreferrer"&gt;&lt;code&gt;pluginlab&lt;/code&gt;&lt;/a&gt;) uses wasmtime, which supports WebAssembly Component Model natively, as its underlying runtime.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pluginlab&lt;/code&gt; implements security and functionality features on top of wasmtime's capabilities, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filesystem sandboxing via flags (&lt;code&gt;--allow-read&lt;/code&gt;, &lt;code&gt;--allow-write&lt;/code&gt;, &lt;code&gt;--dir&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Network access via &lt;code&gt;--allow-net&lt;/code&gt; (you can also whitelist domains/IPs)&lt;/li&gt;
&lt;li&gt;Plugin loading (&lt;code&gt;--plugins&lt;/code&gt;) from local or HTTP sources&lt;/li&gt;
&lt;li&gt;REPL logic injection (&lt;code&gt;--repl-logic&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;http-client&lt;/code&gt; interface is &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/crates/pluginlab/src/store.rs" rel="noopener noreferrer"&gt;implemented using the &lt;code&gt;reqwest&lt;/code&gt; crate&lt;/a&gt; and only works if networking is allowed. Calls to &lt;code&gt;http-client.get()&lt;/code&gt; will fail without &lt;code&gt;--allow-net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;reqwest&lt;/code&gt; crate is async, we need to use &lt;code&gt;wasmtime_wasi::p2::add_to_linker_async&lt;/code&gt; when we link a plugin component so that it will share the same async runtime as the host, otherwise, you will get &lt;code&gt;Cannot start a runtime from within a runtime.&lt;/code&gt; error (see &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/commit/d057660" rel="noopener noreferrer"&gt;d057660&lt;/a&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Handling mismatched versions
&lt;/h4&gt;

&lt;p&gt;To work together, the host and the plugins must share the same version of the WIT file, otherwise, the bindings will not match and you will get a runtime error.&lt;/p&gt;

&lt;p&gt;The CLI host will handle such errors and tell the user to update their cli - more infos on &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/10" rel="noopener noreferrer"&gt;PR#10&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Browser Host (TypeScript)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foj5b6ys1z8nweoci93m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foj5b6ys1z8nweoci93m7.png" alt="Architecture Browser" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since browsers only support Wasm &lt;em&gt;modules&lt;/em&gt;, not &lt;em&gt;components&lt;/em&gt;, we use &lt;a href="https://github.com/bytecodealliance/jco" rel="noopener noreferrer"&gt;&lt;code&gt;jco transpile&lt;/code&gt;&lt;/a&gt; in the build step to convert the compiled &lt;code&gt;.wasm&lt;/code&gt; components into a compatible format for the browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.js&lt;/code&gt; glue code (exports typed functions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.core.wasm&lt;/code&gt; files (transpiled WebAssembly modules)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of call to &lt;code&gt;jco&lt;/code&gt; to transpile a component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jco transpile &lt;span class="nt"&gt;--no-nodejs-compat&lt;/span&gt; &lt;span class="nt"&gt;--no-namespaced-exports&lt;/span&gt; public/plugins/plugin_echo.wasm &lt;span class="nt"&gt;-o&lt;/span&gt; ./src/wasm/generated/plugin_echo/transpiled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Networking
&lt;/h4&gt;

&lt;p&gt;Because &lt;code&gt;jco&lt;/code&gt; doesn’t support async functions yet, the &lt;code&gt;http-client&lt;/code&gt; interface is implemented with a &lt;strong&gt;synchronous&lt;/strong&gt; &lt;code&gt;XMLHttpRequest&lt;/code&gt; wrapper inside the browser host.&lt;/p&gt;

&lt;p&gt;This implementation lives in &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/packages/web-host/src/wasm/host/http-client.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/wasm/host/http-client.ts&lt;/code&gt;&lt;/a&gt;, and is mapped via the build system so that it will be used when the &lt;code&gt;repl:api/http-client&lt;/code&gt; interface is called:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/packages/web-host/vite.config.ts" rel="noopener noreferrer"&gt;vite.config.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/packages/web-host/tsconfig.app.json" rel="noopener noreferrer"&gt;tsconfig.app.json&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Filesystem
&lt;/h4&gt;

&lt;p&gt;Browsers don’t have access to a real filesystem, so a virtual one is injected at runtime. I use &lt;a href="https://github.com/bytecodealliance/jco/blob/main/packages/preview2-shim/lib/browser/filesystem.js" rel="noopener noreferrer"&gt;&lt;code&gt;@bytecodealliance/preview2-shim/filesystem&lt;/code&gt;&lt;/a&gt; to mount an in-memory virtual FS that shims &lt;code&gt;wasi:filesystem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A CLI script &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/packages/web-host/clis/prepareFilesystem.ts" rel="noopener noreferrer"&gt;&lt;code&gt;prepareFilesystem.ts&lt;/code&gt;&lt;/a&gt; generates a JSON structure from a directory. That structure is passed to &lt;code&gt;@bytecodealliance/preview2-shim/filesystem#_setFileData&lt;/code&gt; at runtime so that plugins can access a filesystem without changing their code.&lt;/p&gt;

&lt;p&gt;You can check it out on the &lt;a href="https://topheman.github.io/webassembly-component-model-experiments/#repl" rel="noopener noreferrer"&gt;demo&lt;/a&gt; and try commands like &lt;code&gt;ls&lt;/code&gt; or &lt;code&gt;cat README.md&lt;/code&gt; to see it in action.&lt;/p&gt;

&lt;h4&gt;
  
  
  Local fork of &lt;code&gt;@bytecodealliance/preview2-shim&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;@bytecodealliance/preview2-shim&lt;/code&gt; doesn't support &lt;strong&gt;WRITE&lt;/strong&gt; operations properly out of the box (see issue &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/issues/12" rel="noopener noreferrer"&gt;#12&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I forked the project and added support for WRITE operations, you can check the implementation details in the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/15" rel="noopener noreferrer"&gt;PR#15&lt;/a&gt;, that way, plugins like &lt;code&gt;tee&lt;/code&gt; can write to the filesystem in the browser (see &lt;a href="https://topheman.github.io/webassembly-component-model-experiments" rel="noopener noreferrer"&gt;demo&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  6. REPL Logic
&lt;/h2&gt;

&lt;p&gt;The REPL logic itself is also a Wasm component (&lt;code&gt;repl-logic-guest.wasm&lt;/code&gt;). It handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variable expansion (&lt;code&gt;export VAR=value&lt;/code&gt;, &lt;code&gt;echo $VAR&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Help and man routing&lt;/li&gt;
&lt;li&gt;Plugin dispatching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why a separate REPL logic component? Because it’s &lt;strong&gt;reused between CLI and browser&lt;/strong&gt; — no need to duplicate logic accross platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  WIT definition
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface repl-logic {
  record plugin-response {
    status: repl-status,
    stdout: option&amp;lt;string&amp;gt;,
    stderr: option&amp;lt;string&amp;gt;,
  }

  record parsed-line {
    command: string,
    payload: string,
  }

  variant readline-response {
    to-run(parsed-line),
    ready(plugin-response),
  }

  readline: func(line: string) -&amp;gt; readline-response;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implemented in &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/blob/master/crates/repl-logic-guest/src/lib.rs" rel="noopener noreferrer"&gt;crates/repl-logic-guest&lt;/a&gt; and compiled to a &lt;code&gt;repl-logic-guest.wasm&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Input Flow Example
&lt;/h3&gt;

&lt;p&gt;When the user prompts the REPL, the host sends the input to the &lt;code&gt;readline&lt;/code&gt; function exposed by the &lt;code&gt;repl-logic-guest&lt;/code&gt; component, which returns a &lt;code&gt;readline-response&lt;/code&gt;. Then there are two possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reserved Command&lt;/strong&gt;: If the input is a reserved command (like &lt;code&gt;help&lt;/code&gt;, &lt;code&gt;man&lt;/code&gt;, &lt;code&gt;list-commands&lt;/code&gt; or &lt;code&gt;export&lt;/code&gt; ), the &lt;code&gt;repl-logic-guest&lt;/code&gt; component executes the command directly and returns the output as a &lt;code&gt;readline-response.ready(plugin-response)&lt;/code&gt; variant which will be displayed to the user by the host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin Command&lt;/strong&gt;: Otherwise, the &lt;code&gt;repl-logic-guest&lt;/code&gt; component returns a &lt;code&gt;readline-response.to-run(parsed-line)&lt;/code&gt; variant with the parsed payload to be executed by a plugin.

&lt;ul&gt;
&lt;li&gt;Then the host dispatches this payload to the &lt;code&gt;run&lt;/code&gt; function of the targetted plugin which will return a &lt;code&gt;readline-response.ready(plugin-response)&lt;/code&gt; variant which will be displayed to the user by the host.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This back and forth is necessary because the &lt;code&gt;run&lt;/code&gt; of each plugin is only accessible in the host (not via the &lt;code&gt;repl-logic-guest&lt;/code&gt; component), since it's the host which instantiates the plugins.&lt;/p&gt;

&lt;p&gt;Here is a sequence diagram that illustrates the flow:&lt;/p&gt;

&lt;h4&gt;
  
  
  Reserved Command (&lt;code&gt;help&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zatsmod6iy4dhoryarv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zatsmod6iy4dhoryarv.png" alt="Reserved Command workflow" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Plugin Command (&lt;code&gt;echo Hello&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa1onjg8y1thz61878uz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa1onjg8y1thz61878uz.png" alt="Plugin Command workflow" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These flows show the system's ability to handle both built-in logic and plugin delegation using the same architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Testing and CI/CD
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;The project includes comprehensive end-to-end (e2e) tests that ensure any regression is caught without being tightly coupled to any specific implementation. These tests cover both the terminal REPL and the web interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/tree/master/crates/pluginlab/tests" rel="noopener noreferrer"&gt;Terminal E2E Tests&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;a href="https://crates.io/crates/rexpect" rel="noopener noreferrer"&gt;&lt;code&gt;rexpect&lt;/code&gt;&lt;/a&gt; for testing the CLI REPL behavior&lt;/li&gt;
&lt;li&gt;Tests plugin loading, execution, and error handling&lt;/li&gt;
&lt;li&gt;Ensures the terminal interface works correctly across different scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/tree/master/packages/web-host/tests" rel="noopener noreferrer"&gt;Web E2E Tests&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;&lt;code&gt;Playwright&lt;/code&gt;&lt;/a&gt; for testing the web interface&lt;/li&gt;
&lt;li&gt;Tests plugin functionality in the browser environment&lt;/li&gt;
&lt;li&gt;Validates the web-host integration with WebAssembly components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CI/CD
&lt;/h3&gt;

&lt;p&gt;The project maintains a robust CI/CD pipeline that covers multiple programming languages and targets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-Language Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rust&lt;/strong&gt;: CLI application with WebAssembly target + Plugins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript/Node.js&lt;/strong&gt;: Web interface and tooling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go&lt;/strong&gt;: Plugin implementations with TinyGo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: Plugin implementations with WASI SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkout the source code of the CI/CD pipeline in the &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/tree/master/.github/workflows" rel="noopener noreferrer"&gt;&lt;code&gt;.github/workflows&lt;/code&gt;&lt;/a&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;E2E tests run automatically for both CLI and web targets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deployment Automation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://topheman.github.io/webassembly-component-model-experiments/" rel="noopener noreferrer"&gt;Website&lt;/a&gt; is automatically published on successful builds&lt;/li&gt;
&lt;li&gt;Release drafts are automatically created when git tags are pushed&lt;/li&gt;
&lt;li&gt;Pre-uploaded wasm plugin files are included in releases (that way, plugins from previous versions are still available)&lt;/li&gt;
&lt;li&gt;View releases: &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/releases" rel="noopener noreferrer"&gt;GitHub Releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cross-compilation &amp;amp; Publication to Hombrew tap automation&lt;/strong&gt; &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/17" rel="noopener noreferrer"&gt;PR#17&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When tagged, the cli is cross-compiled to linux/macos - (Intel/ARM)&lt;/li&gt;
&lt;li&gt;Uploaded to a draft release&lt;/li&gt;
&lt;li&gt;When the release is published, the version is published to my Homebrew tap repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Conclusion
&lt;/h2&gt;

&lt;p&gt;This project was a playground to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go deep into the WebAssembly Component Model&lt;/li&gt;
&lt;li&gt;Work with WIT interfaces, sandboxing, and plugin-based architecture&lt;/li&gt;
&lt;li&gt;Share logic between CLI and browser using Wasm components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This project was the &lt;strong&gt;necessary step&lt;/strong&gt; to understand the Wasm Component Model, get familiar with the tooling and the ecosystem and make a real-world project with it.&lt;/p&gt;

&lt;p&gt;The goal was never to build a full-featured shell, but to focus on WASM Components, this project will eventually be the &lt;strong&gt;foundation&lt;/strong&gt; for a more advanced project.&lt;/p&gt;

&lt;p&gt;This project will let me keep experimenting with WebAssembly Component Model as new features, languages, tooling, and versions come out. It's basically my &lt;strong&gt;playground for testing new ideas and concepts&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/topheman/webassembly-component-model-experiments" rel="noopener noreferrer"&gt;topheman/webassembly-component-model-experiments&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A few &lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pulls?q=" rel="noopener noreferrer"&gt;PRs&lt;/a&gt; that are worth checking out:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/6" rel="noopener noreferrer"&gt;PR#6 - Add C plugin support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/16" rel="noopener noreferrer"&gt;PR#16 - Add Go plugin support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/10" rel="noopener noreferrer"&gt;PR#10 - Handle mismatched versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/15" rel="noopener noreferrer"&gt;PR#15 - Add support for WRITE operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/topheman/webassembly-component-model-experiments/pull/17" rel="noopener noreferrer"&gt;PR#17 - Add Homebrew Support and Cross-Compilation Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Website: &lt;a href="https://topheman.github.io/webassembly-component-model-experiments" rel="noopener noreferrer"&gt;topheman.github.io/webassembly-component-model-experiments&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Previous work with WebAssembly:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/topheman/bevy-rust-wasm-experiments" rel="noopener noreferrer"&gt;topheman/bevy-rust-wasm-experiments&lt;/a&gt;: Basic video game in rust that compiles both to a binary file and a web (via WebAssembly, binding the accelerometer and gyroscope sensors from the mobile device)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/topheman/webassembly-wasi-experiments" rel="noopener noreferrer"&gt;topheman/webassembly-wasi-experiments&lt;/a&gt;: Discover WebAssembly System Interface (WASI) with C/Rust targetting NodeJS, python, Wasmtime and the browser&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/topheman/rust-wasm-experiments" rel="noopener noreferrer"&gt;topheman/rust-wasm-experiments&lt;/a&gt;: Discover how to use Rust to generate WebAssembly, called by JavaScript&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=F3wOfWIFzVc&amp;amp;list=PLWhFHBFsRtquZ6hVXVjXmJ-l51ZXuSBtb" rel="noopener noreferrer"&gt;📺🇫🇷 Utiliser WebAssembly, dès aujourd'hui - ParisJS #86&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://component-model.bytecodealliance.org" rel="noopener noreferrer"&gt;Bytecode Alliance - The WebAssembly Component Model&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>webassembly</category>
      <category>wasi</category>
      <category>plugin</category>
      <category>rust</category>
    </item>
    <item>
      <title>React Server Components in Practice: Building a fake E-commerce Site with Next.js 15 latest features</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Tue, 04 Mar 2025 15:17:54 +0000</pubDate>
      <link>https://dev.to/topheman/react-server-components-in-practice-building-a-fake-e-commerce-site-with-nextjs-15-latest-features-73p</link>
      <guid>https://dev.to/topheman/react-server-components-in-practice-building-a-fake-e-commerce-site-with-nextjs-15-latest-features-73p</guid>
      <description>&lt;p&gt;&lt;a href="https://thefakeshop.vercel.app/" rel="noopener noreferrer"&gt;👀 See the demo here&lt;/a&gt; - &lt;a href="https://github.com/topheman/fakeshop" rel="noopener noreferrer"&gt;👨‍💻 See the source code here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the last 18 months, in the react ecosystem, we have been witnessing the rise of server components.&lt;/p&gt;

&lt;p&gt;It's not the only new concept that has been introduced. Metaframeworks like Next.js have been relying for months on alpha/beta/RC versions of react 19, preparing for features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RSC (React Server Components)&lt;/li&gt;
&lt;li&gt;Server Actions&lt;/li&gt;
&lt;li&gt;Streaming&lt;/li&gt;
&lt;li&gt;New caching strategies&lt;/li&gt;
&lt;li&gt;Partial Prerendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stable version of react 19 shipped in December 2024 (see &lt;a href="https://react.dev/blog/2024/12/05/react-19" rel="noopener noreferrer"&gt;their blog post&lt;/a&gt;).&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Experiments&lt;/li&gt;
&lt;li&gt;Features of the project&lt;/li&gt;
&lt;li&gt;Tech stack&lt;/li&gt;
&lt;li&gt;
Implementation

&lt;ul&gt;
&lt;li&gt;Experimental flags&lt;/li&gt;
&lt;li&gt;
Server components

&lt;ul&gt;
&lt;li&gt;Layout&lt;/li&gt;
&lt;li&gt;QR Code Generation&lt;/li&gt;
&lt;li&gt;Header / UserIcon&lt;/li&gt;
&lt;li&gt;Header / SearchCombobox&lt;/li&gt;
&lt;li&gt;Category page&lt;/li&gt;
&lt;li&gt;Search Page / Product Page&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Mixing RSC and client components&lt;/li&gt;

&lt;li&gt;Server actions&lt;/li&gt;

&lt;li&gt;Progressive Enhancement&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Experiments
&lt;/h2&gt;

&lt;p&gt;For the last year, multiple blog posts and videos have been published explaining how you would use those new features (some content even go deeper explaining some implementation details).&lt;/p&gt;

&lt;p&gt;Those are great content, however, they don't address the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should we now go all-in on RSCs and Server Actions? Drop all client components?&lt;/li&gt;
&lt;li&gt;How does this mix with client-side fetching? (react query, etc ...)&lt;/li&gt;
&lt;li&gt;When should you use either one of them?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why I decided to build a real project to experiment on those new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features of the project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://thefakeshop.vercel.app/" rel="noopener noreferrer"&gt;👀 See the demo here&lt;/a&gt; - &lt;a href="https://github.com/topheman/fakeshop" rel="noopener noreferrer"&gt;👨‍💻 See the source code here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a fake e-commerce website, that allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access list of categories of products&lt;/li&gt;
&lt;li&gt;Access products by category&lt;/li&gt;
&lt;li&gt;Access product details&lt;/li&gt;
&lt;li&gt;Search for products&lt;/li&gt;
&lt;li&gt;Add/remove product to/from cart&lt;/li&gt;
&lt;li&gt;View cart&lt;/li&gt;
&lt;li&gt;Login/Logout (a fake identity is generated for you when you login)&lt;/li&gt;
&lt;li&gt;Checkout (a fake payment is made)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It should be SSR friendly (for performance and SEO reasons)&lt;/li&gt;
&lt;li&gt;It should also have a good user experience on client-side (fast navigation, interactivity, etc ...)&lt;/li&gt;
&lt;li&gt;It should take in account progressive enhancement&lt;/li&gt;
&lt;li&gt;Real api calls are made to an external API containing mock data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 15.2 - canary version (some features are not yet available in the stable version)&lt;/li&gt;
&lt;li&gt;React 19&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Shadcn UI&lt;/li&gt;
&lt;li&gt;Vercel (for hosting)&lt;/li&gt;
&lt;li&gt;No database, everything is stored on cookies for simplicity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Experimental flags
&lt;/h3&gt;

&lt;p&gt;I turned on the following experimental flags (which needed the canary version of Next.js):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dynamicIO&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;dynamicIO&lt;/code&gt; flag is an experimental feature in Next.js that causes data fetching operations in the App Router to be excluded from pre-renders unless they are explicitly cached.&lt;/p&gt;
&lt;p&gt;It is useful if your application requires fresh data fetching during runtime rather than serving from a pre-rendered cache.&lt;/p&gt;
&lt;p&gt;It is expected to be used in conjunction with &lt;code&gt;use cache&lt;/code&gt; so that your data fetching happens at runtime by default unless you define specific parts of your application to be cached with &lt;code&gt;use cache&lt;/code&gt; at the page, function, or component level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/api-reference/config/next-config-js/dynamicIO" rel="noopener noreferrer"&gt;Link to documentation (dynamicIO)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ppr&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Partial Prerendering (PPR) enables you to combine static and dynamic components together in the same route.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/api-reference/config/next-config-js/ppr" rel="noopener noreferrer"&gt;Link to documentation (PPR)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the report of the build, you can see most of the routes are partially prerendered as static HTML with dynamic server-streamed content. ◐&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route (app)                              Size     First Load JS
┌ ◐ /                                    214 B           195 kB
├ ○ /_not-found                          140 B           120 kB
├ ◐ /account                             671 B           196 kB
├ ○ /api/hello/world                     140 B           120 kB
├ ƒ /api/og                              353 B           121 kB
├ ◐ /category/[slug]                     1.17 kB         196 kB
├   └ /category/[slug]
├ ◐ /checkout                            207 B           195 kB
├ ◐ /login                               214 B           195 kB
├ ◐ /product/[slug]                      1.04 kB         196 kB
├   └ /product/[slug]
└ ◐ /search                              1.17 kB         196 kB
+ First Load JS shared by all            120 kB
  ├ chunks/520-047851854c706276.js       60.6 kB
  ├ chunks/f5e865f6-9abf91c0747daaa2.js  57.8 kB
  └ other shared chunks (total)          1.92 kB


ƒ Middleware                             33.1 kB

○  (Static)             prerendered as static content
◐  (Partial Prerender)  prerendered as static HTML with dynamic server-streamed content
ƒ  (Dynamic)            server-rendered on demand
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Server components
&lt;/h3&gt;

&lt;p&gt;By default, all components are server components in Next.js App Router, unless you explicitly mark them as client components using the &lt;code&gt;"use client"&lt;/code&gt; directive.&lt;/p&gt;

&lt;p&gt;In RSCs, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch data, access backend resources directly (APIS, databases, file-system, etc ...)&lt;/li&gt;
&lt;li&gt;Keep large dependencies and sensitive data on the server&lt;/li&gt;
&lt;li&gt;Send only the HTML output to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RSCs can be rendered on the server, they can also be rendered at build time - any runtime which doesn't have interactive content.&lt;/p&gt;

&lt;p&gt;I will detail a few use cases I implemented on this project which leverages RSC features with the experimental flags I enabled.&lt;/p&gt;

&lt;p&gt;The server actions part is detailed in the next section.&lt;/p&gt;

&lt;h4&gt;
  
  
  Layout - RSC + PPR = build time generation of static layout
&lt;/h4&gt;

&lt;p&gt;A simplified version of the layout component:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Providers&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;Header&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;Cart&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="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;main&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;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="nc"&gt;Providers&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;Since every components are server components by default, it will return a static HTML shell.&lt;/p&gt;

&lt;p&gt;Since we enabled the &lt;code&gt;ppr&lt;/code&gt; flag, the layout will be rendered at build time, and the HTML will be cached (as it doesn't need to be rendered on each request).&lt;/p&gt;

&lt;p&gt;You still can include client components, like the &lt;code&gt;Cart&lt;/code&gt; component, which will be rendered on the client.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Unrelated:&lt;/em&gt; on this project, I use the &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/route-groups" rel="noopener noreferrer"&gt;Route groups&lt;/a&gt; feature to have a specific layout for the checkout page and a generic one for the rest of the app.&lt;/p&gt;

&lt;h4&gt;
  
  
  QRCode - RSC + PPR + "use cache" = build time generation of static content
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/CustomQRCode.tsx" rel="noopener noreferrer"&gt;src/components/CustomQRCode.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/app/(shop)/page.tsx" rel="noopener noreferrer"&gt;src/app/page.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the home page, I display a QRCode linking to the website, which is rendered by this react component: &lt;code&gt;&amp;lt;CustomQRCode payload="https://thefakeshop.vercel.app/" /&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CustomQRCode&lt;/code&gt; being a server component, it lets us use the &lt;code&gt;toDataURL&lt;/code&gt; from the &lt;a href="https://www.npmjs.com/package/qrcode" rel="noopener noreferrer"&gt;&lt;code&gt;qrcode&lt;/code&gt;&lt;/a&gt; package to generate the QRCode image. Since RSCs can be async, we can &lt;code&gt;await toDataURL&lt;/code&gt; directly in the component.&lt;/p&gt;

&lt;p&gt;That way, we don't need to ship the &lt;code&gt;qrcode&lt;/code&gt; package to the client bundle, the RSC will return the HTML containing the QRCode image (an img tag with a data url).&lt;/p&gt;

&lt;p&gt;Even better: since the url we want our QRCode is static (it won't change based on the request), we can tag the &lt;code&gt;CustomQRCode&lt;/code&gt; with the &lt;code&gt;"use cache"&lt;/code&gt; directive which, when used on a project with &lt;code&gt;ppr&lt;/code&gt; (Partial Prerendering) enabled, &lt;strong&gt;will prerender the QRCode image at build time&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Header / UserIcon - RSC + Suspense + PPR + DynamicIO = streaming
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/Header.tsx" rel="noopener noreferrer"&gt;src/components/Header.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/UserIcon.tsx" rel="noopener noreferrer"&gt;src/components/UserIcon.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;code&gt;Header&lt;/code&gt; component, I use a &lt;code&gt;UserIcon&lt;/code&gt; server component, either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shows a white user icon if the user is not logged in&lt;/li&gt;
&lt;li&gt;shows a green user icon (which links to the account page) if the user is logged in
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Header.tsx - simplified version&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="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;User&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;lucide-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="nx"&gt;UserIcon&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;./UserIcon&lt;/span&gt;&lt;span class="dl"&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;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Rest of the header */&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;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&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;UserIcon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;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;// UserIcon.tsx - simplified version&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;User&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;lucide-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The user svg icon&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&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;getUserInfos&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/session&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;UserIcon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Fetches the user infos server-side&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userInfos&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;getUserInfos&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;Link&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;"/account"&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;User&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userInfos&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-green-300&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="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;Link&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;ol&gt;
&lt;li&gt;At build time, the &lt;code&gt;User&lt;/code&gt; component (which is the fallback for our &lt;code&gt;Suspense&lt;/code&gt; boundary wrapping &lt;code&gt;UserIcon&lt;/code&gt;) is rendered and cached. Thanks to the &lt;code&gt;ppr&lt;/code&gt; flag, it will be rendered as static HTML.&lt;/li&gt;
&lt;li&gt;At runtime, when the server starts streaming the HTML, it first returns this fallback HTML as part as the HTML response.&lt;/li&gt;
&lt;li&gt;While the HTML is being streamed to the client, the &lt;code&gt;UserIcon&lt;/code&gt; server component is rendered on the server.&lt;/li&gt;
&lt;li&gt;If the user is logged in, a fragment of HTML containing the green user icon is streamed to the client which will replace the fallback HTML.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All that &lt;strong&gt;in the same HTTP response&lt;/strong&gt;. And since our "loading" state is just the default &lt;code&gt;User&lt;/code&gt; icon, it doesn't show at all.&lt;/p&gt;

&lt;p&gt;Here is a simplified version of the raw HTML response with comments to explain the streaming part, follow the 🔍 comments to understand the streaming process:&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Fake&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Shop&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/search"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- form for the search combobox (explained in the next section) --&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
                            &lt;span class="c"&gt;&amp;lt;!--$?--&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"B:0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- 🔍 1. default icon --&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lucide lucide-user "&lt;/span&gt; &lt;span class="na"&gt;data-prerender-hint=&lt;/span&gt;&lt;span class="s"&gt;"default icon prerendered"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"7"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/circle&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
                            &lt;span class="c"&gt;&amp;lt;!--/$--&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- cart button --&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hidden&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"S:0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!--- 🔍 2. the logged in user icon which was streamed down to the client --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hover:text-gray-300"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Logged in as Kaley Weimann"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/account"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lucide lucide-user text-green-300"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"7"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/circle&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;// 🔍 3. light runtime that will swap suspense fallback with the streamed content&lt;/span&gt;
            &lt;span class="nx"&gt;$RC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousSibling&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-dgst&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextSibling&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodeType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&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="k"&gt;if &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="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;d&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="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                    &lt;span class="k"&gt;else&lt;/span&gt;
                                        &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                &lt;span class="k"&gt;else&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="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextSibling&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                            &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_reactRetry&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_reactRetry&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="nc"&gt;$RC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;B:0&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;S:0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 🔍 4. swapping the default icon with the streamed logged in icon&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__next_f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__next_f&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&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;script&amp;gt;&lt;/span&gt;
            &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__next_f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Header / SearchCombobox - RSC + Suspense + dynamicIO = progressive enhancement
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/Header.tsx" rel="noopener noreferrer"&gt;src/components/Header.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/SearchCombobox.tsx" rel="noopener noreferrer"&gt;src/components/SearchCombobox.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/SearchComboboxSkeleton.tsx" rel="noopener noreferrer"&gt;src/components/SearchComboboxSkeleton.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;SearchCombobox&lt;/code&gt; relies on &lt;code&gt;@headlessui/react&lt;/code&gt; for the combobox UI, and &lt;code&gt;react-query&lt;/code&gt; for the search request of the autocomplete. So it's a &lt;strong&gt;client component&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Since we use the &lt;code&gt;dynamicIO&lt;/code&gt; flag, NextJS will attempt to render the &lt;code&gt;SearchCombobox&lt;/code&gt; at build time (as it is a client component, on the Layout, which is statically rendered at build time).&lt;/p&gt;

&lt;p&gt;However, since we also rely on &lt;code&gt;useSearchParams&lt;/code&gt; on this component, &lt;a href="https://nextjs.org/docs/app/api-reference/functions/use-search-params#behavior" rel="noopener noreferrer"&gt;we have to wrap it inside a &lt;code&gt;Supense&lt;/code&gt; boundary&lt;/a&gt;.&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;// Header.tsx - simplified version&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="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="nx"&gt;SearchCombobox&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;./SearchCombobox&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;SearchComboboxSkeleton&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;./SearchComboboxSkeleton&lt;/span&gt;&lt;span class="dl"&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;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Rest of the header */&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;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchComboboxSkeleton&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;SearchCombobox&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;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;// SearchCombobox.tsx - simplified version&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SearchCombobox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initialQuery&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// all the client-side logic for the combobox (autocomplete UI, react-query, etc ...)&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;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/search"&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;Combobox&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SearchComboboxSkeleton.tsx - simplified version&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;SearchComboboxSkeleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// the skeleton of the combobox - exactly looks like the `SearchCombobox` component&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;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/search"&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;"q"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Search products..."&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;That way, the fallback for the &lt;code&gt;Suspense&lt;/code&gt; boundary of the &lt;code&gt;SearchCombobox&lt;/code&gt; component will be the &lt;code&gt;SearchComboboxSkeleton&lt;/code&gt; which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;looks just like the &lt;code&gt;SearchCombobox&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;is statically rendered at build time&lt;/li&gt;
&lt;li&gt;doesn't require any client-side JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That way, the search combobox will be usable as soon as this part of the HTML is loaded, even before the whole page or the JavaScript is loaded - &lt;strong&gt;progressive enhancement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once the JavaScript is loaded, the DOM of the &lt;code&gt;SearchComboboxSkeleton&lt;/code&gt; will be replaced with the real &lt;code&gt;SearchCombobox&lt;/code&gt; component which will be interactive.&lt;/p&gt;

&lt;h4&gt;
  
  
  Category Page - RSC + Suspense = streaming
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/app/(shop)/category/[slug]/page.tsx" rel="noopener noreferrer"&gt;src/app/(shop)/category/[slug]/page.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/ProductGridLoading.tsx" rel="noopener noreferrer"&gt;src/components/ProductGridLoading.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/ProductGridSkeleton.tsx" rel="noopener noreferrer"&gt;src/components/ProductGridSkeleton.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The category &lt;code&gt;page.tsx&lt;/code&gt; file exports a sync root component that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wraps a &lt;code&gt;CategoryContent&lt;/code&gt; component with a &lt;code&gt;Suspense&lt;/code&gt; boundary

&lt;ul&gt;
&lt;li&gt;with the &lt;code&gt;dynamicIO&lt;/code&gt; flag, if the &lt;code&gt;page.tsx&lt;/code&gt; exported an async component it would be considered as dynamic and would be rendered on the server everytime&lt;/li&gt;
&lt;li&gt;since we export a sync component, the &lt;code&gt;CategoryContent&lt;/code&gt; will be rendered at build time and cached as static HTML (better for perf)&lt;/li&gt;
&lt;li&gt;the skeleton showed while loading &lt;code&gt;ProductGridLoading&lt;/code&gt; is rendered at build time and cached as static HTML&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;passes the &lt;code&gt;params&lt;/code&gt; to this &lt;code&gt;CategoryContent&lt;/code&gt; component

&lt;ul&gt;
&lt;li&gt;this component is async and handles loading the products by category with &lt;code&gt;getProductsByCategory&lt;/code&gt; at runtime&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Splitting the code like this lets us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;have a fast initial page load (the HTML is rendered at build time)&lt;/li&gt;
&lt;li&gt;stream the products to the client in the same HTTP response (the &lt;code&gt;CategoryContent&lt;/code&gt; component is server-side rendered)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;ProductGrid&lt;/code&gt; component shows the details of each product with a &lt;code&gt;Link&lt;/code&gt; to their detail page&lt;/li&gt;
&lt;li&gt;that way, &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching" rel="noopener noreferrer"&gt;NextJS will prefetch&lt;/a&gt; the detail page of each product, that way, accessing the detail page of a product will be very fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simplified version of the code of the category page:&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;// src/app/(shop)/category/[slug]/page.tsx - simplified version&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;slugToDisplayName&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;@/utils/slugUtils&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Sync root component&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;CategoryPage&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="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&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="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PageContainer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductGridLoading&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;CategoryContent&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&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;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PageContainer&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="c1"&gt;// Async child component&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;CategoryContent&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="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&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="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&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;params&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;products&lt;/span&gt; &lt;span class="p"&gt;}&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;getProductsByCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;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="nf"&gt;slugToDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;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;products&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="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductGrid&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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;No products found in this category.&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;/&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 is the component that handles the loading state of the category page (and the search page). In order to avoid a flash on the title of the page (when it will be replaced by the real title), it is infered from the pathname.&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;// ProductGridLoading.tsx - simplified version&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;slugToDisplayName&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;@/utils/slugUtils&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;ProductGridLoading&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="nx"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/search&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;q&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Search results for "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/category/&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slugToDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;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;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;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="nc"&gt;ProductGridSkeleton&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;h4&gt;
  
  
  Search Page / Product Page - RSC + Suspense = streaming
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/app/(shop)/search/page.tsx" rel="noopener noreferrer"&gt;src/app/(shop)/search/page.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/app/(shop)/product/[slug]/page.tsx" rel="noopener noreferrer"&gt;src/app/(shop)/product/[slug]/page.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/ProductGridLoading.tsx" rel="noopener noreferrer"&gt;src/components/ProductGridLoading.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/components/ProductGridSkeleton.tsx" rel="noopener noreferrer"&gt;src/components/ProductGridSkeleton.tsx&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the render level, it works mostly the same way as the category page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mixing RSCs with client components
&lt;/h3&gt;

&lt;p&gt;Until now, we have been mainly using RSCs.&lt;/p&gt;

&lt;p&gt;Here are the main client components that are used in the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SearchCombobox&lt;/code&gt; - a client component that relies on &lt;code&gt;react-query&lt;/code&gt; to fetch the search results&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddToCartButton&lt;/code&gt; - a client component that displays an icon button to add a product to the cart&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cart&lt;/code&gt; - a client component that displays the cart and allows to update the quantity of the items

&lt;ul&gt;
&lt;li&gt;It shows when you click on the &lt;code&gt;AddToCartButton&lt;/code&gt; component or on the cart icon in the &lt;code&gt;Header&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It relies on &lt;code&gt;react-query&lt;/code&gt; to fetch the cart items and the products&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server Actions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations" rel="noopener noreferrer"&gt;NextJS documentation&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Server Actions are asynchronous functions that are executed on the server. They can be called in Server and Client Components to handle form submissions and data mutations in Next.js applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;No need to mount seperate api routes&lt;/li&gt;
&lt;li&gt;Only call functions from the server actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Server actions are often presented inside &lt;code&gt;&amp;lt;form action={myServerAction}&amp;gt;&lt;/code&gt; usage. Here are two examples on this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login Page: &lt;a href="https://github.com/topheman/fakeshop/blob/master/src/app/(shop)/login/page.tsx" rel="noopener noreferrer"&gt;src/app/(shop)/login/page.tsx&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Checkout Page: &lt;a href="https://github.com/topheman/fakeshop/blob/master/src/app/(checkout)/checkout/page.tsx" rel="noopener noreferrer"&gt;src/app/(checkout)/checkout/page.tsx&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm also using react-query on the client side to handle the cart. Since react-query only expects a function that returns a promise, you can totally mix it with server actions (you don't have to open a new api route, only call functions from the server actions).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/topheman/fakeshop/blob/master/src/hooks/cart.tsx" rel="noopener noreferrer"&gt;See my custom react-query hooks example&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Progressive enhancement
&lt;/h3&gt;

&lt;p&gt;Often, when people talk about progressive enhancement, they talk about the ability to use the page even if JavaScript is disabled.&lt;/p&gt;

&lt;p&gt;With features like RSC streaming that returns the critical HTML first so that the page renders as soon as it loads and swaps the loaded HTML with the final one as soon as the final chunks are loaded, &lt;strong&gt;we need JavaScript to be enabled&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is about performance. Example on this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you land on a category page which takes too long to load&lt;/li&gt;
&lt;li&gt;The header will be sent in the first chunks of the HTML response&lt;/li&gt;
&lt;li&gt;It will contain the JS-free version of the &lt;code&gt;SearchCombobox&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;You will still be able to make a search even if the JavaScript is still loading and hasn't bootstrapped the page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to &lt;code&gt;&amp;lt;form action="/search"&amp;gt;&amp;lt;input name="q" /&amp;gt;&amp;lt;/form&amp;gt;&lt;/code&gt; which doesn't need JavaScript to be enabled.&lt;/p&gt;

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

&lt;p&gt;Server components is a great piece of technology.&lt;/p&gt;

&lt;p&gt;I really learned a lot about caching strategies on NextJS.&lt;/p&gt;

&lt;p&gt;I succeeded in my challenge of mixing server actions and react-query. However, I don't know yet what is the best strategy (maybe like every topic in software development, it depends). If you have any feedback or suggestions, please let me know.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>servercomponents</category>
      <category>serveractions</category>
    </item>
    <item>
      <title>Discover React Suspense</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Sat, 06 Oct 2018 19:30:19 +0000</pubDate>
      <link>https://dev.to/topheman/discover-react-suspense-ml</link>
      <guid>https://dev.to/topheman/discover-react-suspense-ml</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Nj4q2fHulqc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch screencast [en]&lt;/strong&gt; / &lt;a href="http://dev.topheman.com/decouvrir-react-suspense-video-talk-fr/"&gt;Watch talk [fr]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With React v16, the Facebook team shipped a new version of the core called “Fiber”. Thanks to this full rewrite, we’ve already seen new features like Fragments, Portals and ErrorBoundaries. Some other features are still under development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suspense&lt;/li&gt;
&lt;li&gt;Time Slicing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the last few months, engineers from facebook have made a few &lt;a href="https://youtu.be/nLF0n9SACd4" rel="noopener noreferrer"&gt;demonstrations&lt;/a&gt; of the possibilities of these features. Few examples are available (at the time of the writing, those are still unstable/undocumented APIs).&lt;/p&gt;

&lt;p&gt;After the &lt;a href="https://youtu.be/X-kA8B2QzjY" rel="noopener noreferrer"&gt;talk of Ryan Florence at React Rally&lt;/a&gt; presenting Suspense and how &lt;a href="https://github.com/reach/router" rel="noopener noreferrer"&gt;@reach/router&lt;/a&gt; is ready for it, I decided to make a little project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to dive into those new concepts&lt;/li&gt;
&lt;li&gt;to share code examples&lt;/li&gt;
&lt;li&gt;to expose a simple tool that could help explain the benefits of using suspense&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://react-fiber-experiments.surge.sh/" rel="noopener noreferrer"&gt;👉Try topheman/react-fiber-experiments👈&lt;/a&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What problems does Suspense solves ?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It lets you start rendering before all data is ready (rendering can be paused when data starts loading and resumed when it finishes)&lt;/li&gt;
&lt;li&gt;It lets you avoid a cascade of spinners, that may cause flashes due to unintentional reflows and repaints&lt;/li&gt;
&lt;li&gt;Code splitting becomes a built-in feature, easy to use (you also can pause rendering while loading script)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move the fetching of data closer to where it will be rendered – data encapsulation&lt;/li&gt;
&lt;li&gt;Get rid of loading states – cleaner state in components (maybe even stateless in some use cases – no more need of componentDidMount lifecycle hook to go fetch data)&lt;/li&gt;
&lt;li&gt;Finer grain control over spinners (thanks to &lt;code&gt;&amp;lt;Placeholder&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to test it today
&lt;/h3&gt;

&lt;p&gt;Suspense is still under active development, be aware that the APIs might change and be unstable, though it’s advanced enough to hack a project with. You’ll need to make your own build of React.&lt;/p&gt;

&lt;p&gt;Tophe&lt;/p&gt;

&lt;p&gt;This was a short introduction to &lt;strong&gt;React Suspense&lt;/strong&gt;, go further by watching the video and play online with &lt;strong&gt;&lt;a href="https://react-fiber-experiments.surge.sh/" rel="noopener noreferrer"&gt;topheman/react-fiber-experiments&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post was originally posted on &lt;a href="http://dev.topheman.com/"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>suspense</category>
      <category>screencast</category>
    </item>
    <item>
      <title>Finding knowledge is not hard. Putting it all together is.</title>
      <dc:creator>Tophe</dc:creator>
      <pubDate>Fri, 11 May 2018 06:44:14 +0000</pubDate>
      <link>https://dev.to/topheman/finding-knowledge-is-not-hard-putting-it-all-together-is-33ol</link>
      <guid>https://dev.to/topheman/finding-knowledge-is-not-hard-putting-it-all-together-is-33ol</guid>
      <description>&lt;h2&gt;
  
  
  The reason why I made topheman/npm-registry-browser
&lt;/h2&gt;

&lt;p&gt;In software development, a lot of great quality resources are available, often for free. I’ve been getting feedbacks from developers – at work, online, at meetups – who shared that &lt;strong&gt;the hard part is not finding the knowledge&lt;/strong&gt; but &lt;strong&gt;picking one library over an other or putting them all together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tutorials explaining a specific problem are all over there, what’s missing is projects examples / courses with a &lt;strong&gt;wider point of view&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is what I decided to do in my latest project: &lt;a href="https://github.com/topheman/npm-registry-browser" rel="noopener noreferrer"&gt;topheman/npm-registry-browser&lt;/a&gt;. I respect some constraints that you would get, developing a real-world application, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;external API calls&lt;/li&gt;
&lt;li&gt;using external libraries (UI kits, router, http clients …)&lt;/li&gt;
&lt;li&gt;project setup for development with teams&lt;/li&gt;
&lt;li&gt;code quality (linter, code formatting)&lt;/li&gt;
&lt;li&gt;tests (unit / end to end)&lt;/li&gt;
&lt;li&gt;automation / dev pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project itself is a Single Page Application that lets you search for packages in the npm registry and show details for each one of them such as the readme, the versions, the stats … In fact, &lt;strong&gt;this project is just an excuse to expose how to put together all those technologies&lt;/strong&gt; I mentioned above.&lt;/p&gt;

&lt;p&gt;The source code is available on &lt;a href="https://github.com/topheman/npm-registry-browser" rel="noopener noreferrer"&gt;github&lt;/a&gt;. You can test a &lt;a href="https://topheman.github.io/npm-registry-browser" rel="noopener noreferrer"&gt;demo online&lt;/a&gt;. I will be adding more features in the next weeks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://topheman.github.io/npm-registry-browser" rel="noopener noreferrer"&gt;TRY IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; This project is based on &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;create-react-app&lt;/a&gt; and remains &lt;strong&gt;unejected&lt;/strong&gt;. It was &lt;a href="https://github.com/topheman/npm-registry-browser#why-not-eject-" rel="noopener noreferrer"&gt;a constraint&lt;/a&gt; I imposed myself from the start. I never used CRA before (I have my own &lt;a href="https://github.com/topheman/webpack-babel-starter" rel="noopener noreferrer"&gt;webpack starter-kit&lt;/a&gt;), so I wanted to test it to be able to tell what’s possible to do with it and what is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PPS:&lt;/strong&gt; I chose not to use Redux, at least, not in that first version because … &lt;a href="https://github.com/topheman/npm-registry-browser#why-not-use-redux-" rel="noopener noreferrer"&gt;You might not need Redux&lt;/a&gt; (explanation) …&lt;/p&gt;

&lt;p&gt;📺&lt;a href="http://dev.topheman.com/pourquoi-realiser-topheman-npm-registry-browser-video-talk/"&gt;Watch video of the talk (fr)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post was originally posted on &lt;a href="http://dev.topheman.com/"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>frontend</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
