<?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: Ruben Casas</title>
    <description>The latest articles on DEV Community by Ruben Casas (@infoxicator).</description>
    <link>https://dev.to/infoxicator</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%2F476946%2Fcd62b575-8876-4d29-8b32-f42d2ab5d622.jpg</url>
      <title>DEV Community: Ruben Casas</title>
      <link>https://dev.to/infoxicator</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/infoxicator"/>
    <language>en</language>
    <item>
      <title>Chrome Just Dropped Web MCP (and That’s Kind of a Big Deal)</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Fri, 13 Feb 2026 23:48:55 +0000</pubDate>
      <link>https://dev.to/infoxicator/chrome-just-dropped-web-mcp-and-thats-kind-of-a-big-deal-4fa2</link>
      <guid>https://dev.to/infoxicator/chrome-just-dropped-web-mcp-and-thats-kind-of-a-big-deal-4fa2</guid>
      <description>&lt;p&gt;The Chrome team announced experimental support for Web MCP landing in Chrome 146&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2020903127428313461-968" src="https://platform.twitter.com/embed/Tweet.html?id=2020903127428313461"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2020903127428313461-968');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2020903127428313461&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;WebMCP is one of those things that sound super niche… until you realise it’s implications for the future of user / machine interaction are bigger than you think!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“agents can now use the web like we do, but faster and more efficiently!”&lt;/em&gt; 🤖&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But wait... Ai agents can already use websites today using automation tools like Playwright or Puppeteer, so what's the problem?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We’ve spent decades polishing UX for humans.&lt;/li&gt;
&lt;li&gt;Now suddenly Agents are using UX that wasn't built for them!&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What even is Web MCP?
&lt;/h2&gt;

&lt;p&gt;I would like to explain this with a great video I found of a Robotic Arm landing a plane!&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/WkbIGjYwKA0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;It works! but it is not efficient because the machine is using human controls... a more efficient way is let the computers control the plane directly (autopilot) because the computer doesn't need levers and buttons like humans do.&lt;/p&gt;

&lt;p&gt;So Web MCP is basically a plane &lt;strong&gt;autopilot for the web&lt;/strong&gt;. It allows web application to expose functionality as "tools" with natural language descriptions and structured schemas that can be invoked by AI agents. This interaction model is more efficient than expensive and brittle direct DOM manipulation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes web automation... like Playwright but removing the workarounds and "DOM levers" and creating a better standardised &lt;strong&gt;Agent Experience&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  I’ve been playing with this concept since “February last year” (~ 7 Ai years ago)
&lt;/h2&gt;

&lt;p&gt;Last February at Postman (Which in AI time is… basically a geological era. 😅) one of my colleagues created a POC where he connected the Postman User Interface to Cursor via MCP.&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-1950588464480555218-200" src="https://platform.twitter.com/embed/Tweet.html?id=1950588464480555218"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1950588464480555218-200');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1950588464480555218&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;The way this worked was by exposing an MCP Server in our Electron Desktop application that agents could connect to directly. The tools exposed had callbacks that performed side effects on the UI (opening a new tab, sending a request, typing in an input box)&lt;/p&gt;

&lt;p&gt;The limitation here is that we needed a "server" to expose the tools and this server had to be connected to a running local instance of the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web MCP solves the need for a server&lt;/strong&gt; and let's the frontend (JavaScript) register these tools. The concept is very similar to our POC and also is very heavily inspired by MCP itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo vibes: “driving the UI” is cool, but what's the end game here?
&lt;/h2&gt;

&lt;p&gt;Yes, watching an agent drive a UI is cool.&lt;br&gt;&lt;br&gt;
Yes, it makes for great tweets.&lt;br&gt;&lt;br&gt;
Yes, it feels a bit like magic the first time.&lt;/p&gt;

&lt;p&gt;But the real question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this mean for the future of user interaction?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because this isn’t just a new automation trick; it’s a &lt;strong&gt;bridge&lt;/strong&gt; toward the kind of human machine collaboration I’m envisioning. Web MCP isn’t the destination, but it makes the transition possible.&lt;/p&gt;


&lt;h2&gt;
  
  
  The mobile boom déjà vu
&lt;/h2&gt;

&lt;p&gt;I remember when the iPhone exploded in the late 2000s, suddenly every business had a hard choice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make your site mobile-friendly... Or slowly leak users and revenue&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The funny part? We &lt;em&gt;still&lt;/em&gt; have websites in 2026 that are not mobile friendly! and work only on desktop and internal enterprise tools are the slowest worst offenders.&lt;/p&gt;

&lt;p&gt;So when people say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Every website will just add AI / agent support.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or in this case... &lt;strong&gt;every website will add WebMCP support&lt;/strong&gt;... My reaction is: maybe. But also… &lt;strong&gt;probably not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Web MCP is exciting. I just don’t think the future is to “retrofit every website with agent UX.”&lt;/p&gt;


&lt;h2&gt;
  
  
  The real shift: Collapsing Layers
&lt;/h2&gt;

&lt;p&gt;My version of the future of user interaction is all about &lt;strong&gt;collapsing layers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Less:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Human → Agent → UI → Backend → Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intent → System → Result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes with a UI.&lt;br&gt;&lt;br&gt;
Sometimes without.&lt;br&gt;&lt;br&gt;
Sometimes with a UI that’s more like a &lt;em&gt;visualisation&lt;/em&gt; than a control surface.&lt;/p&gt;

&lt;p&gt;Let me break this into three buckets.&lt;/p&gt;


&lt;h2&gt;
  
  
  1) Some websites (and UIs) just… shouldn’t exist
&lt;/h2&gt;

&lt;p&gt;Hot take, but:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The best interface is no interface.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If something the OpenClaw craze taught us is that Agents today can "&lt;strong&gt;just do things&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;If an agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call an API directly&lt;/li&gt;
&lt;li&gt;Or fetch structured data&lt;/li&gt;
&lt;li&gt;Or trigger a workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…then why making it click through 7 screens of UI? Or call 7 different client side tools? that is just latency with extra steps? Those steps were needed for humans, but machines can get the job done without the extra fluff.&lt;/p&gt;

&lt;p&gt;However...&lt;/p&gt;

&lt;p&gt;This doesn’t mean &lt;strong&gt;all&lt;/strong&gt; UI disappears.&lt;/p&gt;

&lt;p&gt;Sometimes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A button click is faster than a prompt&lt;/li&gt;
&lt;li&gt;A form is safer than a fuzzy instruction&lt;/li&gt;
&lt;li&gt;A visual overview beats a wall of text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reality is: UI becomes &lt;em&gt;optional&lt;/em&gt;, not default.&lt;/p&gt;


&lt;h2&gt;
  
  
  2) Go straight to the API (because agents can actually do things)
&lt;/h2&gt;

&lt;p&gt;Everything is an API.&lt;br&gt;&lt;br&gt;
Or at least, everything &lt;em&gt;should&lt;/em&gt; be.&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2020049883311534415-377" src="https://platform.twitter.com/embed/Tweet.html?id=2020049883311534415"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2020049883311534415-377');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2020049883311534415&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;WebMCP is great and much better than the current browser automation tools, but in a hypothetical world where architecture is not purely driven by tradeoffs, costs and business constraints...&lt;/p&gt;

&lt;p&gt;Then the more interesting future is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent talks to the backend API directly&lt;/li&gt;
&lt;li&gt;Backend exposes real capabilities&lt;/li&gt;
&lt;li&gt;Frontend becomes a reactive, visual layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sync engines&lt;/li&gt;
&lt;li&gt;Local First - state-driven UIs&lt;/li&gt;
&lt;li&gt;The UI as a projection, not the source of truth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The UI is no longer “the thing you operate.”&lt;br&gt;&lt;br&gt;
It’s “the thing you look at while things happen.”&lt;/p&gt;

&lt;p&gt;But what about "client state"?&lt;/p&gt;

&lt;p&gt;I don't think there will be a huge portion of surviving "client only state", it will either not be needed or there will be alternatives like "Local first" with a sync engine.&lt;/p&gt;

&lt;p&gt;This future is elegant and exciting... I can only dream!&lt;/p&gt;




&lt;h2&gt;
  
  
  3) True human machine collaboration
&lt;/h2&gt;

&lt;p&gt;This is the part I’m actually most excited about. Not everything is just a visualisation or an action.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You do everything for me.”&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“You do some things. I do some things. We meet in the middle.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think we’ll see the rise of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ai native apps&lt;/li&gt;
&lt;li&gt;Interfaces designed &lt;em&gt;for&lt;/em&gt; machine / human collaboration&lt;/li&gt;
&lt;li&gt;Tools where the agent and the human share context, state, and intent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not just automation. Not just chat. Not just pretty visualisations.&lt;/p&gt;

&lt;p&gt;More like a shared workspace with two kinds of brains in it. A native collaborative canvas.&lt;/p&gt;

&lt;p&gt;A great example of this direction is the recent release of the Excalidraw MCP App:&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2021284377506742331-35" src="https://platform.twitter.com/embed/Tweet.html?id=2021284377506742331"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2021284377506742331-35');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2021284377506742331&amp;amp;theme=dark"
  }





&lt;br&gt;
I love this example because it is where I see the most value in Ui that is not just useful but crucial to unlock the power of machines that will come from a combined collaborative effort.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Web MCP fits into this
&lt;/h2&gt;

&lt;p&gt;If the sections above sounded like I was arguing &lt;em&gt;against&lt;/em&gt; Web MCP, here’s the twist: I’m not. I’m arguing that it’s a &lt;strong&gt;necessary bridge&lt;/strong&gt;, a needed improvement, not the destination. It’s how we move from today’s click-driven web to a world of capabilities, APIs, and collaboration. And we can do it without waiting for a perfect rewrite that’s unlikely to happen anytime soon.&lt;/p&gt;

&lt;p&gt;WebMCP is the perfect tool to align the current web with the new reality of not just humans consuming it.&lt;/p&gt;

&lt;p&gt;But the future of the web and the future of user interaction goes beyond what we can imagine today. Ask someone in the 20s to describe what the future would look like in 100 years because we’re famously bad at predicting real discontinuities they would paint a picture that resembles their current understanding of the world... not today's reality where machines became a fundamental part of our lives.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>agents</category>
      <category>ux</category>
      <category>ai</category>
    </item>
    <item>
      <title>I Reverse Engineered ChatGPT Apps Iframe Sandbox</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Thu, 22 Jan 2026 23:59:40 +0000</pubDate>
      <link>https://dev.to/infoxicator/i-reverse-engineered-chatgpt-apps-iframe-sandbox-2ok3</link>
      <guid>https://dev.to/infoxicator/i-reverse-engineered-chatgpt-apps-iframe-sandbox-2ok3</guid>
      <description>&lt;h2&gt;
  
  
  Before ChatGPT Apps, I Had to Solve The Same Problem!
&lt;/h2&gt;

&lt;p&gt;When I was working on  the first MCP-UI Client in production at Postman, I hit a brick wall fast. MCP-UI apps render untrusted code inside an iframe, and most real-world production applications block third party content and enforce it via their &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP" rel="noopener noreferrer"&gt;Content Security Policy (CSP)&lt;/a&gt;. If the host only whitelists known domains, how do you load arbitrary apps? You do not! That is the point! 😅&lt;/p&gt;

&lt;p&gt;I've been fighting CSPs since my Amex days. We built a &lt;strong&gt;dynamic CSP generator&lt;/strong&gt; for micro‑frontend apps at runtime so each page could assemble the right script-src/frame-src on the fly. That taught me early: &lt;strong&gt;The CSP is never the easy part, but it's the part you can't dodge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I reached out to &lt;a href="https://x.com/liadyosef" rel="noopener noreferrer"&gt;Liad&lt;/a&gt; and &lt;a href="https://x.com/idosal1" rel="noopener noreferrer"&gt;Ido&lt;/a&gt; from MCPI-UI and worked with them to come up with a solution on how to allow untrusted third party apps (the point of MCP-UI) while ensuring the host application remained secure, keeping the host's CSP tight and without having to whitelist the whole internet. The solution was a &lt;strong&gt;double iframe architecture&lt;/strong&gt; safe enough for production. &lt;/p&gt;

&lt;p&gt;here's the demo!&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/qfBlnl89kVM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  The ChatGPT Apps SDK
&lt;/h2&gt;

&lt;p&gt;Fast forward to the ChatGPT Apps SDK release. I found a lot of gaps in the developer experience while building ChatGPT apps, i.e having to connect via an ngrok tunnel, setting up a CDN server that was available to the tunnel, caching issues etc. So my first thought was to &lt;strong&gt;allow ChatGPT apps to render inside Postman&lt;/strong&gt; so you can build and debug them faster! (We are probably one of the only MCP Hosts where it makes sense to render ChatGPT apps outside of ChatGPT).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the video of explaining the DX gaps and how Postman MCP inspector support for chatGPT apps solves the problem&lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/W-OCfM68354"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Hmmm, that Looks familiar 🤔
&lt;/h2&gt;

&lt;p&gt;For this I had to figure out how the ChatGPT Apps SDK client implementation worked, with no public facing documentation! (nobody apart from ChatGPT needs this! 😅). I started poking around the dev console inside ChatGPT and saw a very familiar setup. Same idea: &lt;strong&gt;a sandbox proxy iframe wrapping an inner iframe&lt;/strong&gt;, with a message bridge in the middle! I had already built this once for MCP-UI, so I reverse engineered their flavor and used it to unlock ChatGPT Apps inside Postman.&lt;/p&gt;

&lt;h2&gt;
  
  
  Iframes, &lt;del&gt;magnets&lt;/del&gt; How do they work?
&lt;/h2&gt;

&lt;p&gt;If your host enforces a CSP, you cannot just drop in any third-party iframe &lt;code&gt;frame-src&lt;/code&gt; is an allowlist. An allowlist of "every MCP app that will ever exist" is not a thing.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;you cannot just disable the CSP&lt;/strong&gt;. That is a non starter for security teams. So the only way out is a fixed, trusted domain that the host can allow, and then a controlled way to render the actual app behind it.&lt;/p&gt;

&lt;p&gt;This is the core trick. Without this, MCP‑UI apps were basically dead in real enterprise hosts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The double-iframe trick
&lt;/h2&gt;

&lt;p&gt;The proxy is not a real proxy server. It is a static HTML page hosted on its own origin different to the host's origin. The host only whitelists that one domain. Everything else is controlled by the sandbox iframe.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Host loads the proxy on an allowlisted domain.&lt;/li&gt;
&lt;li&gt;Proxy signals ready; host posts HTML + CSP metadata.&lt;/li&gt;
&lt;li&gt;Proxy creates the inner iframe, sends the HTML via &lt;code&gt;document.write()&lt;/code&gt; and relays messages.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why the double iframe matters
&lt;/h3&gt;

&lt;p&gt;You need two layers to make the security model work:&lt;/p&gt;

&lt;p&gt;1) The outer iframe (sandbox proxy) is on a different origin and sandboxed with a configured CSP&lt;br&gt;
2) The inner iframe hosts the untrusted app with a relaxed CSP&lt;/p&gt;

&lt;p&gt;The CSP can also be enforceable per app. The MCP server (app) can provide a custom CSP to the sandbox.&lt;/p&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%2F70zctciylyf9pgogmcqn.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%2F70zctciylyf9pgogmcqn.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The spec caught up (and improved it)
&lt;/h2&gt;

&lt;p&gt;With the launch of &lt;strong&gt;MCP Apps&lt;/strong&gt;, this architecture is now formalised and accepted as the default security model for third party UI via MCP.&lt;/p&gt;

&lt;p&gt;The MCP Apps working group took the learnings from MCP-UI and ChatGPT apps and added to the spec:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host and sandbox must be different origins&lt;/li&gt;
&lt;li&gt;Sandbox must be &lt;code&gt;allow-scripts allow-same-origin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Proxy must enforce CSP derived from UI metadata&lt;/li&gt;
&lt;li&gt;Proxy must forward messages between host and guest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MCP Apps spec also added the use of metadata on UI resources so a server can declare what it needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;connectDomains&lt;/code&gt; for API calls&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resourceDomains&lt;/code&gt; for scripts, styles, images, fonts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;frameDomains&lt;/code&gt; for nested iframes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;baseUriDomains&lt;/code&gt; for base tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The host then builds CSP headers from that list. If the list is empty, defaults are restrictive. &lt;/p&gt;

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

&lt;p&gt;The CSP is annoying, but it is also the best guardrail we have for untrusted UI. You do not remove it. You configure it! and the double iframe trick was perfect for this use case!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: This is by no means an obscure / innovative technique, it is what most of the industry uses in these cases for untrusted embedded UI. ChatGPT told me that during my research! 😂&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want the deep docs, start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MCP-UI-Org/mcp-ui/blob/main/docs/src/guide/client/using-a-proxy.md" rel="noopener noreferrer"&gt;MCP-UI proxy flow and CSP details&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx" rel="noopener noreferrer"&gt;MCP Apps spec (sandbox proxy section)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mcp</category>
      <category>chatgpt</category>
      <category>security</category>
      <category>csp</category>
    </item>
    <item>
      <title>The Reality of AI Coding in Production (and My Poor Man’s Setup)</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Sat, 10 Jan 2026 23:52:23 +0000</pubDate>
      <link>https://dev.to/infoxicator/my-poor-mans-ai-coding-setup-jan-2026-4lnf</link>
      <guid>https://dev.to/infoxicator/my-poor-mans-ai-coding-setup-jan-2026-4lnf</guid>
      <description>&lt;p&gt;For the past few weeks, My timeline has been filled with a lot of developers showcasing (and showing off) the projects they built during the holidays using claude code and the insane progress and that both Opus 4.5 and Codex 5.2 have made to &lt;strong&gt;"solve software engineering"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2009161942339080204-484" src="https://platform.twitter.com/embed/Tweet.html?id=2009161942339080204"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2009161942339080204-484');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2009161942339080204&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;If you feel FOMO, overwhelmed, don't know where to start... &lt;strong&gt;you are not alone!&lt;/strong&gt; This stuff is moving so fast that is normal to feel a bit of whiplash... but fear not, this piece of advice is still very relevant:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Keep it simple… just talk to it!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the current way I build with Ai at work, this is what works for me... for &lt;strong&gt;production code&lt;/strong&gt; that cannot be "slop", that needs to be reviewed and approved by co-workers and that has to deal with the realities of production software, like large brownfield codebases, trade offs, business priorities and tech debt...&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats / Constraints Working with Production Software
&lt;/h2&gt;

&lt;p&gt;The way I use AI to code on a greenfield project, differs significantly from coding on old / production codebases. Models can now work effectively in large codebases, but there are constraints we should aware of and improvements to the code itself that you can make and could increase speed and output considerably.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spaghetti Code
&lt;/h3&gt;

&lt;p&gt;Models that have to traverse large parts of unrelated code bloats the context window, is slow and token inefficient. Reducing circular dependencies and encapsulating components, functions, modularity, great boundaries and architecture has ever been more important!&lt;/p&gt;

&lt;p&gt;If your application is rotten, it will also rot your context!&lt;/p&gt;

&lt;h3&gt;
  
  
  To Rewrite or Not Rewrite
&lt;/h3&gt;

&lt;p&gt;When models context windows fill, they use something called &lt;strong&gt;"context compaction"&lt;/strong&gt;. I can use the same analogy with old codebases, when they fill up with tech debt and old decisions that are no longer relevant, sometimes is better to capture the intent, summarise the current state and commit it into a new session (some people call this a "rewrite") and guess what! it's never been cheaper to rewrite something!&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing Verification Loop
&lt;/h3&gt;

&lt;p&gt;Models that cannot verify their work, require more steering and cannot work continuously. Human verification is ever more important but that verification needs to be committed and saved (via tests, lint rules, type checking, agent context) for future changes. Models are very good at writing tests! but they will not replace the true verification which is when humans decide if the system is "correct" or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Chicken and Egg paradox
&lt;/h3&gt;

&lt;p&gt;Code that is not modular is hard to test, missing tests break the verification loop which produces code that is difficult to change and test. To break this vicious cycle, the code needs to be tested (human verified and that committed) and closing the verification loop to ensure new code and improvements can be safely added by humans and coding agents in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code is not the bottleneck anymore, verification is!
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“If your pull request doesn’t contain evidence that it works, you’re not shipping faster - you’re just moving work downstream”  - Addy Osmani&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Agents can produce reliable code fast, ensuring that code is correct and it does what is intended without breaking the rest of the system is now the bottleneck. The major culprits include code reviews, manual testing and slow automated testing. Improvements to spedup and unblock these areas will improve the velocity of shipping code to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Kitchen Sink paradox
&lt;/h3&gt;

&lt;p&gt;Models are like humans if they see a dirty sink they will just add another plate on top!. Models will not refactor and make the code better unless you tell them to. Every time you make a change, also ask the model to ship an improvement to that part of the code especially around verification loops (test, types etc)&lt;/p&gt;

&lt;h3&gt;
  
  
  Risk of change is a limiting factor
&lt;/h3&gt;

&lt;p&gt;Code is cheap now, we should try more things and not be afraid of significant changes and improving existing code just because it is “old” or too long / complex to understand. The problem with changing too many things in a established codebase / product is the risk of regressions. Tight verification loops are key and could mitigate the risk. But even with excellent testing practices (which are rare) more code, more changes, means more bugs, more risks which businesses need to accept.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Actually Use Ai Coding In My Day Job
&lt;/h2&gt;

&lt;p&gt;With those constraints in mind, this is how I actually apply Ai in my daily work, not for demos or side projects, but inside real production codebases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is not a guide... all of these are subjective and matter of preference&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  My Setup
&lt;/h2&gt;

&lt;p&gt;I use &lt;strong&gt;both Codex and Claude Code&lt;/strong&gt; and use them in parallel or interchangeably depending on the task.&lt;/p&gt;

&lt;p&gt;I use the VSCode / Cursor IDE &lt;strong&gt;extensions&lt;/strong&gt; one on each side, easier to add context and see the code and make adjustments. &lt;/p&gt;

&lt;p&gt;I rarely use Cursor Agent Mode or Composer these days. (Even if you use Opus 4.5 there, &lt;strong&gt;the harness matters&lt;/strong&gt; and I get better results with Claude Code CLI or the extensions). &lt;/p&gt;

&lt;p&gt;There are still some features in Cursor that I find handy, like browser editing especially useful for frontend coding.&lt;/p&gt;

&lt;p&gt;I run both at the same time on different parts of the code or a complementary task and often ask Codex to review Claude’s work and make changes to it with follow up prompts.&lt;/p&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%2Fxtl68vs10x094uqtyaux.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%2Fxtl68vs10x094uqtyaux.png" alt="How my IDE looks like"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;IDE Extensions! Blasphemy! Get out of here! 😂 stay  with me!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex GPT 5.2 (thinking high)
&lt;/h3&gt;

&lt;p&gt;Codex doesn’t require a lot of context, planning or complex prompts it usually gets it right first time with the right pointers but it is &lt;strong&gt;very, very slow!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I use it mostly for complex tasks that require deep thinking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Complex bugs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strong types &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strong data accuracy (convert or scrape data without hallucinations)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code review&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refactors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Explaining the codebase or the code&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Favourite Follow up Prompts:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Is there anything we should refactor?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplify this file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This works but are there any maintenance, security, architecture issues&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This works but is there a better way of achieving the same outcome? show me options&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Claude Code / Opus 4.5
&lt;/h3&gt;

&lt;p&gt;Claude Code with Opus 4.5 is very fast and accurate. It sometimes requires more handholding than codex. It will be very eager to start implementation and will sometimes ask you questions and add a “plan”. You can also trigger “plan mode” and create it manually. I have mixed results using plan mode, I like it, but for established codebases I prefer smaller encapsulated changes + Codex usually works better even without a plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I use it for most coding tasks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;New features &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adding tests / types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating React Components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quick prototypes&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Favourite Prompts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Design an interface for a library&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement a similar method based on this pattern&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a component to do X (it will one shot it with great UX)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rename this function / file and update all references and tests&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Claude Code and Codex CLIs
&lt;/h2&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%2Fayd3bcubfwp7bu7b88ke.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%2Fayd3bcubfwp7bu7b88ke.png" alt="Claude and Codex CLIs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also have the CLIs installed! Allegedly they have a more powerful harness and some features make it first to the CLIs and the IDE extensions lag behind.&lt;/p&gt;

&lt;p&gt;Working with the IDE extensions is a single threaded task where you need to steer the models constantly, look at every single line of code produced, make inline changes to the code test and verify. The limitation is you can only have one concurrent session at the time (unless you clone the repo into different folder and have a separate IDE window, which I do sometimes)&lt;/p&gt;

&lt;p&gt;With the CLIs you can do more &lt;strong&gt;parallel hands off work&lt;/strong&gt; (don’t confuse this with “Vibe coding” you still need to verify, read the code and ensure it does what is intended) &lt;/p&gt;

&lt;p&gt;They also require you to think about conflicts and branching / worktrees stuff if you have them working concurrently on the same codebase. Surprisingly sometimes they stay clear of each other but is not always the case. I find this a faff and avoid it unless I really want to push to do a lot of things at the same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not all types of work and repositories are suited for parallel coding with CLIs&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  I have found them useful in the following cases:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Throw away POCs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migrations (see Ralph Loop coding)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As a starting point before transferring to hands on in the editor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well defined work in projects with strong verification loops (tests)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ralph Loop Coding
&lt;/h2&gt;

&lt;p&gt;The technique everyone is talking about... I started experimenting with it and it seems like the end game for hands off / parallel automated work. &lt;/p&gt;

&lt;p&gt;Codex is really good at one shot single prompt small migrations but for longer, more complex and repeatable tasks (think what codemods used to do poorly) running the CLIs in a loop could produce incredible results. &lt;/p&gt;

&lt;p&gt;The trick is to make the model work on one single thing at the time stopping, starting a new session (after committing and saving context), picking the next thing and so on. effectively &lt;strong&gt;“hacking the context window”&lt;/strong&gt;. The main prerequisite is a &lt;strong&gt;strong verification loop (tests)&lt;/strong&gt; a well defined problem with a clear outcome and a very detailed plan (PRD style doc) that splits the work into small chunks. without these, this technique is not only not effective at all but also very &lt;strong&gt;wasteful and token inefficient!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I tried the claude code built-in plugin... and was very disappointed! It kept &lt;strong&gt;killing my memory&lt;/strong&gt; even when restarting and during the first iteration!&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2008511712060993564-69" src="https://platform.twitter.com/embed/Tweet.html?id=2008511712060993564"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2008511712060993564-69');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2008511712060993564&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;But the Claude plugin doesn't make it justice... If you want to see the comparison between the two and really understand the subtle but big difference when running the loop inside the harness vs outside watch this great video from Chief Wiggum himself, creator or Ralph:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/O2bBWDoxO4s"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Miscellaneous / Tips and Tricks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What about MCPs?&lt;/strong&gt; (use them only when needed. i.e the chrome / playwright when writing frontend code, Context7 for documentation etc)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What about Cursor Rules, Agents.md, Claude.md?&lt;/strong&gt; Models are getting better at finding the right context themselves but if they keep making the same mistake over and over, then it is time to add it to the .MD rules files. (theres' a nice gh action that could be triggered during code review to update those)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Voice Mode:&lt;/strong&gt; I started using Wisprflow more but still like typing so I can order my thoughts &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context Window:&lt;/strong&gt; you shouldn’t worry too much these days (Codex is very good, Claude has auto-compact) but I recommend starting a new session as many times as possible to reset your context window (without loosing context of course)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automate repetitive tasks:&lt;/strong&gt; Claude Code supports slash commands and subagents to automate repetitive tasks (push commit, review, tests etc)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;You are not behind, keep it simple, just give it a go and improve everyday... anyway... it sounds better when it comes from Sunil:&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2007842239012126805-590" src="https://platform.twitter.com/embed/Tweet.html?id=2007842239012126805"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2007842239012126805-590');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2007842239012126805&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://x.com/bcherny/status/2007179832300581177" rel="noopener noreferrer"&gt;Boris Claude Code Setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sunilpai.dev/posts/seven-ways/" rel="noopener noreferrer"&gt;where good ideas come from for coding agents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://steipete.me/posts/just-talk-to-it" rel="noopener noreferrer"&gt;Just Talk To It - the no-bs Way of Agentic Engineering&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aihero.dev/tips-for-ai-coding-with-ralph-wiggum" rel="noopener noreferrer"&gt;How to Ralph&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://addyosmani.com/blog/code-review-ai/" rel="noopener noreferrer"&gt;AI writes code faster. Your job is still to prove it works&lt;/a&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ralph</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Side Projects? I don't have time for that!</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Thu, 08 Jan 2026 23:25:51 +0000</pubDate>
      <link>https://dev.to/infoxicator/side-projects-i-dont-have-time-for-that-57j0</link>
      <guid>https://dev.to/infoxicator/side-projects-i-dont-have-time-for-that-57j0</guid>
      <description>&lt;h2&gt;
  
  
  The importance of Side Projects
&lt;/h2&gt;

&lt;p&gt;When I was doing my Masters degree I used to come back from my part time job at 7pm and start working on side projects until 11pm. I didn't even have time for cooking so I used to get a chinese takeaway and start playing with whatever new technology I was interested in.&lt;/p&gt;

&lt;p&gt;Proof of how many fortune cookies I collected in those days in a single week 😅&lt;br&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%2Fkegl51puprjd7tmyemmy.JPG" 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%2Fkegl51puprjd7tmyemmy.JPG" alt="Fortune Cookies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's why I recommend to anyone getting started in tech to do things on the side, I learned more from side projects than the stuff I learned during my Master's degree which I had to pay £13k for!&lt;/p&gt;
&lt;h2&gt;
  
  
  Time? I don't know that Pokemon!
&lt;/h2&gt;

&lt;p&gt;Fast forward a few jobs and a family and I found less and less time to do things on the side, I kept blogging and learning things but not at the same pace and consistency than those early days. It is not like I didn't want to, I felt guilty sometimes but I also learned that you should prioritise family and other things which are more important.&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-1965143376422207757-163" src="https://platform.twitter.com/embed/Tweet.html?id=1965143376422207757"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1965143376422207757-163');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1965143376422207757&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;However, this year I found the joy of building things, I still don't have time but thanks to Ai and coding agents I found that spending only a few hours has a great compounding effect!&lt;/p&gt;

&lt;p&gt;Gergely from The Pragmatic Engineer puts it nicely: &lt;/p&gt;

&lt;p&gt;| "I get a lot more work done on my side projects, with far less effort" &lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2006301134236135708-333" src="https://platform.twitter.com/embed/Tweet.html?id=2006301134236135708"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2006301134236135708-333');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2006301134236135708&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;The tipping point for me was the release of Codex (and Claude Code). All I needed was an idea (not even a good one, just some half baked thoughts) and a few hours some time just a few minutes each night to achieve great outcomes. &lt;/p&gt;

&lt;p&gt;All of those unfinished projects from the wishlist finally found someone who could complete them (the clanklers... not me!)&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-1989473815983001613-253" src="https://platform.twitter.com/embed/Tweet.html?id=1989473815983001613"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1989473815983001613-253');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1989473815983001613&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;h2&gt;
  
  
  Too Much Talk, Show me the stuff!
&lt;/h2&gt;

&lt;p&gt;Here are the things I shipped, with some small amount of effort in the second half of this year (2025). Working between 10pm and 12am while my wife and the kids are asleep and still spending an unhealthy amount of time on twitter:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Weather Agent with MCP-Ui
&lt;/h3&gt;

&lt;p&gt;I have been playing with MCP-UI since it was launched and wanted to sprinkle a little bit of generative UI on top. So I built a weather agent that gets the current temperature and styles it on the fly with any style provided by the user i.e Windows 95 retro&lt;/p&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%2Fjvi0t78xhpx184f1eobd.jpeg" 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%2Fjvi0t78xhpx184f1eobd.jpeg" alt="Weather Agent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postman Flows&lt;/li&gt;
&lt;li&gt;MCP-UI&lt;/li&gt;
&lt;li&gt;Weather API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Youtube Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=z1fD9-1R_rw" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=z1fD9-1R_rw&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Time Machine with Nano Banana!
&lt;/h3&gt;

&lt;p&gt;The Launch of Nano banana definitely changed my Vibe Coding game, I built most of my prototypes with it and pushed my imagination. I built a website that allows you to transport you back in time just by uploading your picture:&lt;/p&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%2F3ymibdt2pwxe4eao4tej.jpeg" 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%2F3ymibdt2pwxe4eao4tej.jpeg" alt="Nano Banana Time Machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://iwasthere.today/" rel="noopener noreferrer"&gt;www.iwasthere.today&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Youtube Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=y9dDVLVIGTA" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=y9dDVLVIGTA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React + TypeScript&lt;/li&gt;
&lt;li&gt;Cloudflare Workers + R2 Storage&lt;/li&gt;
&lt;li&gt;Postman Flows&lt;/li&gt;
&lt;li&gt;Nano Banana (Gemini Flash Image 2.5)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Shopify Store MCP-Ui.shop
&lt;/h3&gt;

&lt;p&gt;I didn't think that in 2025 I would launch my own e-commerce store, but here we are! One of the most compelling use cases of MCP-Ui (MCP Apps) is e-commerce. so I decided to launch my own shop to sell t-shirts that can be purchased using MCP and any of the clients that support MCP-UI&lt;/p&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%2Fr1a3906zx2vnyykinasf.jpeg" 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%2Fr1a3906zx2vnyykinasf.jpeg" alt="t-shirts from the shop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://mcp-ui.shop/" rel="noopener noreferrer"&gt;https://mcp-ui.shop/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Youtube Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=cM6nYANYxFc" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=cM6nYANYxFc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify&lt;/li&gt;
&lt;li&gt;MCP StoreFront&lt;/li&gt;
&lt;li&gt;MCP-Ui&lt;/li&gt;
&lt;li&gt;Goose&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4. Children's Book: "The Adventures of Fluffy"
&lt;/h3&gt;

&lt;p&gt;I vibecoded a book from scratch for my 4 year old who's obsessed with her stuffed dog. She came up with the story, the locations, we generated the images together and then stitched it up using Canva. This was a great time bonding with my kiddo and showing her what's possible (anything) with technology. We have created 3 books now!&lt;/p&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%2Fm9omhwgwmanz63lhuaj2.jpeg" 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%2Fm9omhwgwmanz63lhuaj2.jpeg" alt="The Adventures of Fluffy in Japan"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nano Banana&lt;/li&gt;
&lt;li&gt;Gemini Pro&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;li&gt;Canva&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  5. TBPN Video Generator
&lt;/h3&gt;

&lt;p&gt;My friend &lt;a href="https://x.com/TobinSouth" rel="noopener noreferrer"&gt;Tobin South&lt;/a&gt; nerd snipped me one night, after he created a picture of the tbpn style announcements for &lt;a href="https://x.com/liadyosef" rel="noopener noreferrer"&gt;Liad&lt;/a&gt; and &lt;a href="https://x.com/idosal1" rel="noopener noreferrer"&gt;Ido&lt;/a&gt; joining Monday.com so I decided to build a TBPN generator but not for images but for video! &lt;br&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%2Fqpyy41itaqjvdi0ha4p0.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%2Fqpyy41itaqjvdi0ha4p0.png" alt="Tobin Slack Thread"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It creates a comedy story for when someone joins a company and generates a slide show on the fly using "generative UI / Video". I also ended up adding a bunch of templates including one for when you &lt;a href="https://tbpn-video-generator.vercel.app/react-summit" rel="noopener noreferrer"&gt;attend a conference&lt;/a&gt; and you meet someone in person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://tbpn-video-generator.vercel.app/" rel="noopener noreferrer"&gt;https://tbpn-video-generator.vercel.app/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Youtube Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=42B_ia64QZU&amp;amp;t=4s" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=42B_ia64QZU&amp;amp;t=4s&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React + Typescript&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;Remotion&lt;/li&gt;
&lt;li&gt;Postman Flows&lt;/li&gt;
&lt;li&gt;Nano Banana&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  6. ChatGPT App to buy T-Shirts
&lt;/h3&gt;

&lt;p&gt;When ChatGPT launched apps, was a great moment of validation for MCP-UI! I decided to build my first app on top of my Shopify Store and allow people not just to buy the t-shirts, but also try them on, with some Ai magic ✨&lt;/p&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%2Ffare81vna9jpja15vw1m.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%2Ffare81vna9jpja15vw1m.png" alt="ChatGPT Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Youtube Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=XHgxGczwERo" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=XHgxGczwERo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NextJS&lt;/li&gt;
&lt;li&gt;ChatGPT Apps SDK&lt;/li&gt;
&lt;li&gt;Postman Flows&lt;/li&gt;
&lt;li&gt;Nano Banana&lt;/li&gt;
&lt;li&gt;Shopify Headless CMS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  7. Postman Welcome Video
&lt;/h3&gt;

&lt;p&gt;After building the TBPN Video generator, I decided to make something a bit more useful with it. So when someone new joins postman, there is a welcome message that gets posted in one of the public slack channels. When I add the 📽️ emoji reaction to the message. An automated workflow takes the person who joined information (profile picture, job title, etc) summarises the message and creates a personalised welcome video.&lt;/p&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%2Fstn6xfk27k4ukd1cxqbl.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%2Fstn6xfk27k4ukd1cxqbl.png" alt="Example Welcome Video"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://tbpn-https://postman-new-hire-video.vercel.app/share/d50704b2-8e56-4a42-87c4-b59148f78c9c" rel="noopener noreferrer"&gt;https://postman-new-hire-video.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React + Typescript&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;Remotion&lt;/li&gt;
&lt;li&gt;Postman Flows&lt;/li&gt;
&lt;li&gt;Nano Banana&lt;/li&gt;
&lt;li&gt;Slack API&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  8. City Quest: The First Original ChatGPT Game!
&lt;/h3&gt;

&lt;p&gt;I joined the TanStack Start Hackathon as the push I needed to finally build an idea I came up with during my first visit to NYC early in the year. The result was this ChatGPT App game, which I think is the first of its kind. It helps players discover their city with ChatGPT as their companion while collecting quests and getting a personalised Instagram ready to share video at the end.&lt;/p&gt;

&lt;p&gt;I played it with &lt;a href="https://x.com/esthor" rel="noopener noreferrer"&gt;Erik from Code Rabbit&lt;/a&gt; and a few friends after AI Engineer conference and we had a ton of fun! we even rescued people stuck in a lift and ended up singing Karaoke.&lt;/p&gt;

&lt;p&gt;A newly added feature was "Kitze Mode" to conquer all the fast food places in America with &lt;a href="https://x.com/thekitze" rel="noopener noreferrer"&gt;TheKitze&lt;/a&gt; as your coach! 😂&lt;/p&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%2Fid0g38zzo53o90mzzae7.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%2Fid0g38zzo53o90mzzae7.png" alt="City Quest"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://city-quest.netlify.app" rel="noopener noreferrer"&gt;https://city-quest.netlify.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Youtube Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=MJ5RtWxOiZc&amp;amp;t=30s" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=MJ5RtWxOiZc&amp;amp;t=30s&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TanStack Start&lt;/li&gt;
&lt;li&gt;ChatGPT Apps SDK&lt;/li&gt;
&lt;li&gt;Convex&lt;/li&gt;
&lt;li&gt;Nano Banana&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  9. My Personal Site
&lt;/h3&gt;

&lt;p&gt;I finally decided to rewrite my personal site, the previous one was embarrassing, not maintained and on a broken version of NextJ 10.  I refused to do it for a long time until I had an innovative idea but at the end, I applied the rule of side projects and vibe coding. Shipping something is better than shipping nothing... hopefully I will include cool easter eggs and ideas to my site... but I am quite pleased with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://infoxicator.com/" rel="noopener noreferrer"&gt;https://infoxicator.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TanStack Start&lt;/li&gt;
&lt;li&gt;Netlify&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There are a few learnings and a few caveats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;** Compounding is powerful!**&lt;br&gt;
Every project builds on top of the one before... the stack the learnings the ideas can be combined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't over do it!&lt;/strong&gt;&lt;br&gt;
I started to over do it sometimes... even vibecoded on a plane at some point but it was because I was enjoying it too much. Suffice to say it could become a problem, always prioritise family and other things...&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fik53ivg2w51fi02ww9vi.jpeg" 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%2Fik53ivg2w51fi02ww9vi.jpeg" alt="Vibe Coding on a Plane"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There's always time!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During the holidays I decided to intentionally take a break so I could play &lt;em&gt;Ghost of Yotēi&lt;/em&gt; so it is always about what you want to prioritise and what's important&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-1996343569456697721-273" src="https://platform.twitter.com/embed/Tweet.html?id=1996343569456697721"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1996343569456697721-273');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1996343569456697721&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;I finished the game now, so don't worry my Vibe Coding career is safe! and onto 2026!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Rethinking UI in Agent Driven Systems</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Thu, 24 Jul 2025 19:38:20 +0000</pubDate>
      <link>https://dev.to/infoxicator/rethinking-ui-in-agent-driven-systems-10nd</link>
      <guid>https://dev.to/infoxicator/rethinking-ui-in-agent-driven-systems-10nd</guid>
      <description>&lt;p&gt;Chat interfaces have surged in popularity, becoming a common entry point to intelligent systems. But while text excels at expressiveness and simplicity, it's often limiting for structured interactions. Booking a meeting, selecting options, or reviewing results all demand more than just words. The immediate solution that comes to mind is to &lt;strong&gt;render traditional user interfaces (components)&lt;/strong&gt; inside the agentic system, but what does that look like in practice?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the viable strategies for embedding UI into chat-driven workflows?&lt;/strong&gt; And even better: &lt;strong&gt;should you be embedding UI at all?&lt;/strong&gt; If so, will these new approaches eventually replace traditional interfaces, or simply augment them?&lt;/p&gt;

&lt;p&gt;In this article, we are going to look at the three emerging strategies for returning UI from agents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First-party UI: Component rendering from within the agent’s codebase&lt;/li&gt;
&lt;li&gt;Third-Party UI: Server-generated and delivered via MCP&lt;/li&gt;
&lt;li&gt;Generative UI: Let the LLM create interfaces on the fly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These modes reflect not just technical choices but broader design philosophies about how software should be delivered when the interface becomes a dialogue, not a static representation of data.&lt;/p&gt;

&lt;p&gt;Each approach solves the "how do I show a button" problem differently. In fact, they solve different problems altogether, but it’s easy to mix and confuse these approaches due to their similarities.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. First-Party UI Rendering: Components from Within
&lt;/h2&gt;

&lt;p&gt;This is the simplest to understand and implement if you own both the agent and the application frontend.&lt;/p&gt;

&lt;p&gt;The tool (or function) returns a UI descriptor that directly maps to a component you already have in the client codebase. In some systems, this means passing back a component name and props. In others, it may be fully serialised  elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example: a tool response returning a UI&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool-response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getUserApproval&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{...},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ApprovalBanner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dangerous command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fhuoyn0ms6ac10j29wzkk.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%2Fhuoyn0ms6ac10j29wzkk.png" alt="Approval Ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This flexibility not only allows for reusing and rendering existing UI, but also unlocks new interaction patterns like &lt;strong&gt;"driving the UI from the agentic system."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of what this looks like in practice:&lt;/p&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%2Fhkrcn9dm65skdzbfc8v5.gif" 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%2Fhkrcn9dm65skdzbfc8v5.gif" alt="video of ai driven ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This demo showcases the agent driving the user interface like if it was a human agent. It opens a requests in a new tab, changes some scripts, sends the request and then displays the results in a nicely formatted component.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://youtu.be/Ph5BBBuEYV0" rel="noopener noreferrer"&gt;Full resolution Video&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Total control:&lt;/strong&gt; You can render any UI the client app already supports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature parity:&lt;/strong&gt; Complex interactivity (modals, animations, hooks) are possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple for internal tools:&lt;/strong&gt; Tools can assume the client knows how to handle specific components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling:&lt;/strong&gt; Bespoke and custom-built. It only works in the client that implements it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard to externalise:&lt;/strong&gt; You cannot expose these tools to third-party systems via MCP or otherwise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State and serialisation:&lt;/strong&gt; To reconstruct the UI from previous conversations, components must be stateless. They need to rebuild state from the data stored in the conversation and be passed as props.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ideal for:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Web apps shipping their own agentic interfaces inside their product&lt;/li&gt;
&lt;li&gt;Agents that need to “drive” the UI and take steps on the user’s behalf&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Postman Agent Mode&lt;/li&gt;
&lt;li&gt;Cursor&lt;/li&gt;
&lt;li&gt;TLDraw Agent Mode&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Third-Party UI: Server-Generated UI Delivered via MCP
&lt;/h2&gt;

&lt;p&gt;The rise of the MCP protocol has made it easier for third-party tools to plug into assistant ecosystems, enabling remote invocation and integration. However, one limitation is that most implementations today focus on returning plain text. That works well for many workflows, but it quickly breaks down when richer user interaction is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if a third-party MCP tool wants to return a map, an interactive widget, or a custom data viewer? That's where third-party UI comes in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of returning components, the tool responds with plain HTML or &lt;strong&gt;structured and serialised components&lt;/strong&gt; that describe what should be rendered. The client, which speaks the schema format, interprets and renders the UI. This adds an interactive layer on top of otherwise text-only conversations, unlocking a much richer experience.&lt;/p&gt;

&lt;p&gt;Frameworks like &lt;strong&gt;mcp-ui&lt;/strong&gt;, &lt;strong&gt;Adaptive Cards&lt;/strong&gt;, and &lt;strong&gt;Remote DOM&lt;/strong&gt; are converging here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The server describes the &lt;em&gt;what&lt;/em&gt;, and the client decides &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Plain text example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ui://my-component/instance-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mimeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Hello World&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remote resource example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ui://analytics-dashboard/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mimeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text/uri-list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://my.analytics.com/dashboard/123"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remote DOM example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ui://remote-component/action-button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mimeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.mcp-ui.remote-dom+javascript; framework=react"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  const button = document.createElement('ui-button');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  button.setAttribute('label', 'Click me for a tool call!');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  button.addEventListener('press', () =&amp;gt; {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    window.parent.postMessage({ type: 'tool', payload: { toolName: 'uiInteraction', params: { action: 'button-click', from: 'remote-dom' } } }, '*');&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  });&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  root.appendChild(button);&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This schema uses the official MCP specification. There is a &lt;a href="https://github.com/orgs/modelcontextprotocol/discussions/287" rel="noopener noreferrer"&gt;proposal under discussion&lt;/a&gt; to add a specific type of resource for UI, however the &lt;code&gt;mcp-ui&lt;/code&gt; library is full spec compliant.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The client receives the response from an MCP tool invocation, reconstructs the UI, and renders it in the agent’s host using the preferred rendering method. The client ensures the UI is mounted inside a secure and sandboxed environment like an iframe or a worker.&lt;/p&gt;

&lt;p&gt;To match styling, reuse component libraries, and support frontend frameworks of choice, tools like Remote DOM help bridge the gap while keeping the server implementation generic.&lt;/p&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%2Fjryehyj89qgie47wtvmr.gif" 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%2Fjryehyj89qgie47wtvmr.gif" alt="video of mcp ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This demo uses the &lt;code&gt;mcp-ui&lt;/code&gt; library to send UI components from a shopify store via the Model Context Protocol (MCP)&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://youtu.be/OINfHJF5aEc" rel="noopener noreferrer"&gt;Full resolution video here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extensible:&lt;/strong&gt; Builds on top of existing schema and technologies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateless:&lt;/strong&gt; UI components can be serialised and sent across the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client agnostic:&lt;/strong&gt; As long as the client supports the schema, tools that return UI can be used by any client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited expressiveness:&lt;/strong&gt; App-specific interactions are difficult to support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling constraints:&lt;/strong&gt; Custom theming and layout are limited and harder to implement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authoring complexity:&lt;/strong&gt; The MCP server must serialise components to comply with the schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ideal for:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Existing MCP tools that want to enhance user experience beyond text.&lt;/li&gt;
&lt;li&gt;Products with plugin ecosystems.&lt;/li&gt;
&lt;li&gt;E-commerce and agentic systems where tools don’t have direct access to the frontend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP-UI:&lt;/strong&gt; Shopify Store Front MCP servers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Postman MCP Inspector&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Generative UI:
&lt;/h2&gt;

&lt;p&gt;This is the most speculative and also the most exciting approach. Instead of explicitly returning UI, the LLM generates it directly from the conversation context, injecting the data into interactive elements, not just for display, but for action.&lt;/p&gt;

&lt;p&gt;What makes generative UI especially powerful is how it mirrors the trend of &lt;strong&gt;vibe coding&lt;/strong&gt; where users or agents compose small applications on the fly to solve personalised tasks. This goes beyond prompting or one-off responses. It becomes a way of assembling functionality contextually.&lt;/p&gt;

&lt;p&gt;With the right schema and execution model, generative UI isn’t just a mockup, it becomes a live surface inside the agentic loop: capable of executing functions, calling tools, validating input, and even interacting with third-party systems via the MCP protocol.&lt;/p&gt;

&lt;p&gt;Imagine a scenario where the agent, based on context and goals, produces a multi-step workflow UI part form, part calendar, part dashboard. The user fills it in, submits it, and the agent takes action, all within a single conversational pass.&lt;/p&gt;

&lt;p&gt;This bridges the gap between unstructured input and structured actions. It unlocks a middle layer of interaction too fluid for hardcoded UIs and too structured for plain text.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User: "I want to send flowers to someone in Berlin on Tuesday"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The LLM could generate a UI like so:&lt;/p&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%2Fpkp62iqdhy1vf1pzo7la.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%2Fpkp62iqdhy1vf1pzo7la.png" alt="generated ui for flowers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generative UI will most likely be implemented as an extension of first-party UI due to the control and sandboxing requirements involved in running generated code safely. An interesting approach could involve using &lt;strong&gt;&lt;a href="https://modelcontextprotocol.io/docs/concepts/sampling" rel="noopener noreferrer"&gt;“sampling”&lt;/a&gt;&lt;/strong&gt; from the Context Model Protocol specification to dynamically request UI from the active agent.&lt;/p&gt;

&lt;p&gt;That said, third-party tools can also leverage generative UI. Backend systems can use LLMs to create UIs on the fly and return the resulting schema or code as a valid response—so long as it complies with what the client expects.&lt;/p&gt;

&lt;p&gt;This demo by &lt;a href="https://x.com/Baconbrix" rel="noopener noreferrer"&gt;Evan Bacon&lt;/a&gt; uses an LLM to interpret natural language queries and determine which components to display based on the responses.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/djhEgxQf3Kw?start=677"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexible:&lt;/strong&gt; The same agent can generate different UIs based on context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emergent UX:&lt;/strong&gt; Users help shape the flow rather than being constrained by predefined templates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated orchestration:&lt;/strong&gt; Enables LLMs to drive workflows with third-party tools via standard protocols.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validation overhead:&lt;/strong&gt; Generated UIs must be schema-validated for safety.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fragility:&lt;/strong&gt; LLMs may hallucinate structure or mislabel schema fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging challenges:&lt;/strong&gt; Behavior is non-deterministic and hard to trace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ideal for:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visualizations:&lt;/strong&gt; Let users view data in a format that suits them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible interaction:&lt;/strong&gt; Move beyond static forms to dynamic UIs tailored in real time.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Choosing a Strategy
&lt;/h2&gt;

&lt;p&gt;You don't need to pick one. These strategies are complementary and solve different use cases.&lt;/p&gt;

&lt;p&gt;At Postman, the agentic system we’re building supports a mix of all three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First-party UI:&lt;/strong&gt; React components in Postman Agent Mode display code editors, visualizations, approval flows, and deterministic workflows via tool call chaining.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party UI:&lt;/strong&gt; Using the &lt;code&gt;mcp-ui&lt;/code&gt; package to render UI from MCP servers. This supports both our Agent Mode and the MCP Inspector tool for debugging UI responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generative UI:&lt;/strong&gt; We're exploring this space with our “Flows agent” that dynamically generates UI consumed by our Agent Mode.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Just like  designers had to rethink shape and granularity in the age of mobile, we now need to rethink UI in the age of agents.&lt;/p&gt;

&lt;p&gt;Not every assistant needs to return UI. But once it does, the architecture behind that decision becomes a core part of your product surface. It affects what tools you can build, how partners can extend your system, and how safely and reliably users can interact with it.&lt;/p&gt;

&lt;p&gt;Ooops! nearly forgot… I didn’t dive deep into the question &lt;strong&gt;should you be embedding UI at all?&lt;/strong&gt; That probably deserves a separate article.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading &amp;amp; Inspirations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://julian.digital/2025/03/27/the-case-against-conversational-interfaces/" rel="noopener noreferrer"&gt;The Case Against Conversational Interfaces&lt;/a&gt; by Julian Lehr&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aifoc.us/embedding/" rel="noopener noreferrer"&gt;Embedding UI with Language Agents&lt;/a&gt; by Venkatesh Rao&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mcpui.dev/" rel="noopener noreferrer"&gt;MCP-UI&lt;/a&gt; by &lt;a href="https://x.com/liadyosef" rel="noopener noreferrer"&gt;Liad Yousef&lt;/a&gt; and &lt;a href="https://x.com/idosal1" rel="noopener noreferrer"&gt;Ido Salomon&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>frontend</category>
      <category>react</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Introducing a New Web Vital: Time to Usability (TTU)</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Tue, 11 Mar 2025 21:58:56 +0000</pubDate>
      <link>https://dev.to/infoxicator/introducing-a-new-web-vital-time-to-usability-ttu-13n3</link>
      <guid>https://dev.to/infoxicator/introducing-a-new-web-vital-time-to-usability-ttu-13n3</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introducing a New Web Vital: Time to Usability (TTU)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When thinking about application performance, the go to strategy is to "&lt;strong&gt;Measure, Measure, Measure&lt;/strong&gt;." But not all metrics are created equal. Tools like &lt;strong&gt;Lighthouse&lt;/strong&gt; and &lt;strong&gt;Core Web Vitals&lt;/strong&gt; provide a great high-level view and are useful for debugging, but they &lt;strong&gt;don’t capture performance issues specific to your app’s core functionality&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;What if we had a metric that measured &lt;strong&gt;when users can start using your app&lt;/strong&gt;, not just when the page loads, but when it becomes &lt;strong&gt;truly usable&lt;/strong&gt;? That’s why I’m introducing &lt;strong&gt;Time to Usability (TTU)&lt;/strong&gt;: a new way to measure performance that focuses on &lt;strong&gt;real user experience&lt;/strong&gt; rather than just technical readiness.  &lt;/p&gt;

&lt;p&gt;In this guide, I’ll show you how to use the &lt;strong&gt;Performance API&lt;/strong&gt; to instrument your app, break down every millisecond and capture &lt;strong&gt;TTU&lt;/strong&gt; to optimize performance where it really matters.  &lt;/p&gt;

&lt;h2&gt;
  
  
  How to Measure TTU:
&lt;/h2&gt;

&lt;p&gt;Not everything that can be measured matters when applied to your application context. Traditional performance metrics like &lt;strong&gt;First Contentful Paint (FCP)&lt;/strong&gt; or &lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt; tell us when content appears on screen, but they don’t indicate when an app is actually &lt;strong&gt;usable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To measure TTU, you can ask the following question: How quickly can users start using the app’s core feature?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some real-world examples&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;VS Code&lt;/strong&gt; → How fast until the user gets a &lt;strong&gt;blinking cursor&lt;/strong&gt; and can start coding?&lt;br&gt;
• &lt;strong&gt;Slack&lt;/strong&gt; → How fast until the user can open a chat and &lt;strong&gt;send a message&lt;/strong&gt;?&lt;br&gt;
• &lt;strong&gt;Postman&lt;/strong&gt; → How fast until the user can &lt;strong&gt;send an API request&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Unlike generic performance scores, &lt;strong&gt;TTU&lt;/strong&gt; is application-specific. It shifts the focus from technical implementation details to real usability, helping developers measure what truly impacts user experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;How TTU Differs from Time to Interactive (TTI)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I can hear you thinking, hold on a minute, we already have a metric for that is called &lt;strong&gt;Time to Interactive (TTI)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both &lt;strong&gt;Time to Usability (TTU)&lt;/strong&gt; and &lt;strong&gt;Time to Interactive (TTI)&lt;/strong&gt; measure when an app becomes usable, but they focus on different aspects:  &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Metric&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What It Measures&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to Interactive (TTI)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;When the page is fully interactive (event handlers registered, main thread idle).&lt;/td&gt;
&lt;td&gt;Ensures the app won’t feel sluggish when users try to interact.&lt;/td&gt;
&lt;td&gt;A page can be "interactive" but still not &lt;strong&gt;functional&lt;/strong&gt; for its core task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to Usability (TTU)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;When users can start using the app’s &lt;strong&gt;main feature&lt;/strong&gt; (e.g., sending a message in Slack, coding in VS Code).&lt;/td&gt;
&lt;td&gt;Aligns performance measurement with &lt;strong&gt;real-world usability&lt;/strong&gt;.&lt;/td&gt;
&lt;td&gt;Requires &lt;strong&gt;application-specific instrumentation&lt;/strong&gt;—generic tools don’t track it automatically.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While &lt;strong&gt;TTI ensures technical readiness&lt;/strong&gt;, &lt;strong&gt;TTU reflects real usability&lt;/strong&gt; and helps developers optimize what &lt;strong&gt;actually matters to users&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Instrumenting Your App with Performance Markers
&lt;/h2&gt;

&lt;p&gt;One of the best places to start is &lt;strong&gt;app launch performance&lt;/strong&gt;, the one thing every single user experiences and directly impacts the first impression and usability.&lt;/p&gt;

&lt;p&gt;The Performance API’s mark and measure methods let us place custom timestamps at key points in the user journey and calculate the time between them.&lt;/p&gt;
&lt;h3&gt;
  
  
  Collect the Data Using &lt;code&gt;performance.mark()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Just like adding &lt;code&gt;console.log()&lt;/code&gt; statements to debug your code, you can use &lt;strong&gt;performance markers&lt;/strong&gt; to log how long key operations take. The &lt;strong&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark" rel="noopener noreferrer"&gt;Performance API’s &lt;code&gt;mark&lt;/code&gt; method&lt;/a&gt;&lt;/strong&gt; allows you to timestamp specific points in your app’s execution.&lt;/p&gt;

&lt;p&gt;As an example, we’ll divide the app launch process into &lt;strong&gt;five key phases&lt;/strong&gt; and then aggregate them into &lt;strong&gt;Time to Usability (TTU)&lt;/strong&gt;:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Platform Initialization&lt;/strong&gt; – App container, browser, or runtime initialization. (As an example, in an Electron App, this is the main process bootstrap until the renderer process initialises)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downloading, Parsing &amp;amp; Compiling JavaScript&lt;/strong&gt; – First page load, downloading and executing scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetching Data&lt;/strong&gt; – Making API calls to retrieve necessary data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering the Critical Flow&lt;/strong&gt; – Displaying the UI needed for interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core Feature Ready&lt;/strong&gt; – When the main feature is rendered and fully usable (TTU).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s how you can track &lt;strong&gt;each phase of app launch&lt;/strong&gt; using &lt;code&gt;performance.mark()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Mark platform initialization start&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;platform_init_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Simulate platform setup&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupPlatform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;platform_init_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// JavaScript loading phase&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js_loading_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadJavaScriptBundles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js_loading_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Fetching data phase&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch_data_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchInitialData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch_data_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Rendering the critical flow&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;render_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;renderCriticalUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;render_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Mark when core feature is ready (TTU)&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;core_feature_ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Aggregate total TTU&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Platform Initialization&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;platform_init_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;platform_init_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JS Processing&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;js_loading_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js_loading_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&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 Fetching&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;fetch_data_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch_data_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rendering&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;render_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;render_end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Time to Usability (TTU)&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;platform_init_start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;core_feature_ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Log all performance measures&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;measures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;measure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;measures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;ms`&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;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%2F4eqc236wg70elge24zyu.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%2F4eqc236wg70elge24zyu.png" alt="TTU Breakdown" width="800" height="203"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also see the performance markers using Chrome Developer Tools under the Performance tab -&amp;gt; Timings when running a performance profile.&lt;/p&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%2F8pc4mki3zzmaw0c25707.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%2F8pc4mki3zzmaw0c25707.png" alt="Performance Profile Tab" width="559" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Measuring performance locally is useful, but real-world performance &lt;strong&gt;varies across devices, networks, and user environments.&lt;/strong&gt; To get meaningful insights, you need to send this data to a telemetry, logging or analytics platform where you can aggregate, analyze, and visualize it.&lt;/p&gt;

&lt;p&gt;For example, you can send the collected TTU data to an observability tool or a custom logging service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Page Load Performance with The Resource and Navigation Performance Entries
&lt;/h2&gt;

&lt;p&gt;Once you have identified the bottlenecks, you can go deeper. For example, inspecting the page navigation and the resources (i.e JavaScript assets) that are downloaded during the initial page load.&lt;/p&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%2F7jy6x430dk92c35f6cct.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%2F7jy6x430dk92c35f6cct.png" alt="Navigation timing" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Measuring Document Load with PerformanceNavigationTiming&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming" rel="noopener noreferrer"&gt;PerformanceNavigationTiming&lt;/a&gt; API gives you a detailed breakdown of the navigation process, from the first request to when the page is fully loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`DNS Lookup: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainLookupEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domainLookupStart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`TCP Connection: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectStart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`TTFB (Time to First Byte): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`DOM Load: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domContentLoadedEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Page Load: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&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;2. Measuring Individual Resources with performance.getEntriesByType('resource')&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Beyond document load times, you can track how long each JavaScript file, CSS, image, or API request takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resource&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;ms`&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 especially useful for tracking third-party scripts, which can slow down page performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Advanced: Identifying Network vs. Service Worker Loads with Server Timing API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A more advanced technique is using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceServerTiming" rel="noopener noreferrer"&gt;Server-Timing header&lt;/a&gt; to tag responses. An example use case is to differentiate network loads (cache misses) vs. service worker cache loads (cache hits).&lt;/p&gt;

&lt;p&gt;The Service Worker can add a custom header to all requests that are intercepted and served from it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Server-Timing: cache-hit&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Loaded from Service Worker"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And retrieve this information for each resource (asset downloaded by the page)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resource&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverTiming&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; loaded via: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverTiming&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="nx"&gt;description&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the use cases, but you can also send a custom server timing header to &lt;strong&gt;capture database read/write operation times, CPU time, and file system access.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Synthetic Monitoring vs. Real User Monitoring (RUM)
&lt;/h2&gt;

&lt;p&gt;Performance isn’t a one-time optimization, it’s an ongoing process. This is where Synthetic Monitoring and Real User Monitoring (RUM) complement each other.&lt;/p&gt;

&lt;p&gt;• RUM gives you real-world data from actual users, but it’s a lagging metric, it tells you what happened after users experience an issue.&lt;br&gt;
• Synthetic Monitoring proactively runs tests in controlled environments, helping detect regressions before they impact users.&lt;/p&gt;

&lt;p&gt;The good news? &lt;strong&gt;The performance markers are useful in both Synthetic tests and Real User Metrics&lt;/strong&gt;, you just need to send the data to your telemetry systems.&lt;/p&gt;

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

&lt;p&gt;The &lt;strong&gt;bad news?&lt;/strong&gt; There are a couple of caveats to keep in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Performance API itself has a small overhead. While minimal, instrumenting too many markers or collecting excessive data could impact performance. However, the benefits far outweigh the cost, the insights you gain will help you optimize the real bottlenecks in your app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring TTU is a manual and custom process. Unlike generic performance metrics, Time to Usability (TTU) requires thoughtful instrumentation based on your app’s core functionality. There’s no one-size-fits-all solution, it’s up to you to define and measure what truly matters for your users.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the Performance API, you have the tools to go beyond generic benchmarks and truly understand what makes your app feel fast. By combining custom markers, resource timing, and telemetry, you can continuously measure, optimize, and improve your app’s real-world performance.&lt;/p&gt;

&lt;p&gt;Performance isn’t just about numbers, it’s about user experience! 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>performance</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Embracing Public Speaking: From Fear to Stage</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Fri, 19 Jul 2024 12:06:09 +0000</pubDate>
      <link>https://dev.to/infoxicator/embracing-public-speaking-from-fear-to-stage-1921</link>
      <guid>https://dev.to/infoxicator/embracing-public-speaking-from-fear-to-stage-1921</guid>
      <description>&lt;p&gt;Recently my dear friend &lt;a href="https://x.com/kentcdodds" rel="noopener noreferrer"&gt;Kent C. Dodds&lt;/a&gt; posted a video encouraging people to attend conferences and get into public speaking to supercharge their careers.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1811059773359673721-725" src="https://platform.twitter.com/embed/Tweet.html?id=1811059773359673721"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1811059773359673721-725');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1811059773359673721&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But what if you have never done it before or cannot imagine yourself standing on a stage talking to a large crowd?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am not a perfect speaker or an expert, but I love sharing my passion and knowledge with others. &lt;strong&gt;Let me walk you through my journey&lt;/strong&gt; and share some insights on how you can make your mark in the world of conferences and meetups.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First Steps into Public Speaking
&lt;/h2&gt;

&lt;p&gt;I began my journey in 2020, an awesome year to get into public speaking! 😅. I didn't start on a big stage or big events, I started by reaching out to communities and participating in online meetups. My friend &lt;a href="https://x.com/nialljoemaher" rel="noopener noreferrer"&gt;Niall Maher&lt;/a&gt; trusted me (not sure how!) and invited my colleague &lt;a href="https://x.com/nellykiboi" rel="noopener noreferrer"&gt;Nelly&lt;/a&gt; and me to speak about Micro-Frontends at the &lt;a href="https://x.com/coducommunity" rel="noopener noreferrer"&gt;Codu Community&lt;/a&gt; meetup, now one of the largest meetups in Ireland and the world. I am sure only my colleagues and a couple of hard-core followers of Niall &lt;a href="https://www.youtube.com/live/r92CUWlDwgE?si=AAp6hedxhupu6_O_" rel="noopener noreferrer"&gt;watched it live&lt;/a&gt;, &lt;strong&gt;but hey! it was a start!&lt;/strong&gt;&lt;/p&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%2Ff7jed7v1731s2viw6j2s.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%2Ff7jed7v1731s2viw6j2s.png" alt="Codu Meetup" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Your Unique Style
&lt;/h2&gt;

&lt;p&gt;I got invited to a couple of "big-name" online conferences. For me it was easier to present live, I am not very good at doing pre-recorded talks, however, if you feel more comfortable doing a recording, it is a great way to get started, meet the organisers and discover the process of applying, preparing and delivering a talk. Some conferences have online days where you can apply to speak and you might get more chances to get accepted, but you get to be part of the in-person day and might even get invited to the speaker's dinner 😉.&lt;/p&gt;

&lt;p&gt;There are a lot of people like you and me out there trying to do the same, so standing out can be challenging. Finding your style is important, for example, Kent C. Dodds signature style is to do "&lt;a href="https://x.com/swyx/status/1577963914461159426" rel="noopener noreferrer"&gt;air squats&lt;/a&gt;" before a talk. I started using hand-drawn animations in my presentations to explain complex topics, but even more important is to &lt;strong&gt;be yourself&lt;/strong&gt;, what you have to say is important to the audience and they would love to hear it!&lt;/p&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%2Fee09cqwkwcanyhptt53w.gif" 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%2Fee09cqwkwcanyhptt53w.gif" alt="gif hand drawn animation" width="600" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Engaging Your Audience
&lt;/h2&gt;

&lt;p&gt;The content of your presentation is the most important piece. If you don't know what to talk about, just talk about something cool, something that you are passionate about or a recent experience that you had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tell a story!&lt;/strong&gt; the best presentations are the ones that follow the "hero's journey" and where you tell the audience how you overcame difficulty and implemented a cool technology that provided value to the company you work for instead of a "how to" tutorial and not just delivering a lecture.&lt;/p&gt;

&lt;p&gt;Engagement is also key! I like to be dynamic and energetic on stage, you can use humour to connect with the audience, and body language to present yourself as open by extending your arms, you can ask questions, or even "doing squats" can help maintain that vital connection.&lt;/p&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%2Fq81c00n6eo2p8lhqx9mi.jpeg" 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%2Fq81c00n6eo2p8lhqx9mi.jpeg" alt="Open Arms" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcoming the nerves
&lt;/h2&gt;

&lt;p&gt;I get really nervous on stage, I get physically sick, and my stomach hurts for a good hour after a talk. &lt;strong&gt;It is normal&lt;/strong&gt;, even seasoned speakers will tell you that those nerves never go away. When you choose the right topic and you know very well what you are talking about, it becomes easier and helps you get into the "flow" state. &lt;/p&gt;

&lt;h3&gt;
  
  
  Here are my tips on how to get rid of the nerves:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Practical Tip&lt;/strong&gt;: If you are right-handed clench your left fist as hard as you can, it is scientifically proven that it helps to calm down the nerves.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Mind Tip&lt;/strong&gt;: If you feel like running away, just remember, what you are about to tell the audience could change their careers or even their lives, if you don't do it, they will miss out. You are on a mission, don't think about going back until that mission is complete because &lt;strong&gt;what you have to say is important.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reflecting on Your Performance
&lt;/h2&gt;

&lt;p&gt;I like reviewing the recordings of my talks to make my next presentation even better. After my first on-stage presentation at React Advanced in London, I watched the recording and I realised I sounded like I was telling people off! 😂 I was so passionate about the topic that I sounded like my grandmother warning me why using Micro-Frontends was a bad idea!&lt;/p&gt;

&lt;p&gt;The worst thing you can do as a speaker is to make the audience uncomfortable. So it is essential not to appear aggressive or attack the audience. I also realised my pacing was off, I gave my back to the audience a couple of times and noticed all those "umms" and "amms" that are still there today, so &lt;strong&gt;I strongly recommend reviewing your recordings.&lt;/strong&gt;&lt;/p&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%2Fichk1a9y8oa2s34j0536.jpeg" 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%2Fichk1a9y8oa2s34j0536.jpeg" alt="Speaking at cloud conf" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What If Public Speaking is Not for You?
&lt;/h2&gt;

&lt;p&gt;If you have stage fright or simply are not interested in public speaking, then get involved in other ways. Help organise the meetup or the conference, become part of the technical committee and help review the proposals, or it could be as simple as turning up early and putting the chairs out or setting up the refreshments. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Helping out and being part of the organisers is the best role&lt;/strong&gt;, you don't have to worry about delivering a talk, everybody knows you, everyone is looking up to you and you can make great connections as well.&lt;/p&gt;

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

&lt;p&gt;If you have never tried speaking at conferences, &lt;strong&gt;give it a go&lt;/strong&gt;! and get out there, you don't need to be a professional, none of us are! &lt;/p&gt;

&lt;p&gt;If you tried and you didn't like it, go find a local meetup or contact a conference organiser and offer to give them a hand, I am sure they would be grateful for the extra help.&lt;/p&gt;

&lt;p&gt;Also, remember to have fun! what I love the most about conferences is the amazing people I get to hang out with and have amazing discussions about not just tech but also about life.&lt;/p&gt;

&lt;p&gt;See ya at the next one! 👋&lt;br&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%2F7ts4jrc9mfcp26hj84zf.jpeg" 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%2F7ts4jrc9mfcp26hj84zf.jpeg" alt="Friends" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>My Experience at JSNation and React Summit Amsterdam</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Tue, 18 Jun 2024 13:52:33 +0000</pubDate>
      <link>https://dev.to/infoxicator/my-experience-at-jsnation-and-react-summit-amsterdam-4cbb</link>
      <guid>https://dev.to/infoxicator/my-experience-at-jsnation-and-react-summit-amsterdam-4cbb</guid>
      <description>&lt;p&gt;This was my first time at the biggest JavaScript Festival and React Conference in the world (not an exaggeration). It was also my first time as an attendee and I enjoyed not having to worry about giving a talk and just experiencing the conference!&lt;/p&gt;

&lt;p&gt;Apart from photobombing the stage TVs every single time I had a chance 😅, I was also lucky enough to have &lt;strong&gt;1 on 1 conversations&lt;/strong&gt; with the smartest and most influential speakers and library authors in our industry. &lt;/p&gt;

&lt;h3&gt;
  
  
  Here's what we talked about!
&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%2Fye312n4sw8730gutq49d.jpg" 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%2Fye312n4sw8730gutq49d.jpg" alt="My profile pic on the tv screens" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ryan Carniato, Creator of SolidJS
&lt;/h2&gt;

&lt;p&gt;Ryan and I discussed the “&lt;strong&gt;missing link&lt;/strong&gt;” in the evolution of client-side applications. There is still a gap between Multiple Page applications (MPAs) and Single Page Applications (SPAs) with client-side routing where we are still forced to choose one or the other. There have been improvements with Astro and React Server Components but they still have a performance degradation element that stops it from being complete. My question to him was if the current stack is not enough for most use cases (sites that are not e-commerce and so sensitive to performance impact) his answer was (paraphrasing): &lt;strong&gt;"Mediocrity should not stop progress".&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;He is doing some work on Solid Start that might bring a breakthrough in this area, so I am really excited about what he's cooking 👀. We also discussed migrations, how difficult it is for a company to adopt a new technology and how the landscape is very different from when React came out. Applications are getting old and we are due to a new wave of technologies that use the best parts of what we learned in the past 10 years.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1801517170850967767-982" src="https://platform.twitter.com/embed/Tweet.html?id=1801517170850967767"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1801517170850967767-982');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1801517170850967767&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Tobias K, Creator of Webpack
&lt;/h2&gt;

&lt;p&gt;I had a long chat with Tobias about improving chunking in large applications to reduce bundle size. We also discussed the lack of a visualisation tool to represent the module graph. I am using the tool he created &lt;a href="https://webpack.github.io/analyse/" rel="noopener noreferrer"&gt;https://webpack.github.io/analyse/&lt;/a&gt; but it hasn't been maintained for a while and he acknowledged it doesn’t work well with large stats files.&lt;/p&gt;

&lt;p&gt;I also asked of course, &lt;strong&gt;what's the status of TurboPack&lt;/strong&gt;, his thoughts on RSPack and support for Module Federation. He was very diplomatic and asked me to keep an eye out for later this year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dominik, Maintainer of React Query
&lt;/h2&gt;

&lt;p&gt;Spend a lot of time with Dominik walking around Amsterdam and &lt;strong&gt;helping him #FreeSuspense 😂&lt;/strong&gt;. Apart from having fun, missing the walking tour and trying to find presents for our kids, we also had the chance to talk about Local First applications, State Management in large apps, how we at Postman are pushing React Query to the limit and our custom implementation of "Mobx Query" and "Broadcast client". He asked me to write some articles about those usages of React Query that we could showcase and share with the community. It was also great to be there during the conversation with Sathya from the React Core team at Meta when the decision was made to hold React 19 to ensure the client-side suspense data fetching story was correctly implemented. &lt;strong&gt;React history was made and I was there for it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(Special thanks to Dominik for recommending me the best present for my daughter, what a legend!)&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1801669381182685508-233" src="https://platform.twitter.com/embed/Tweet.html?id=1801669381182685508"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1801669381182685508-233');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1801669381182685508&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Evan Bacon, Creator of Expo Router
&lt;/h2&gt;

&lt;p&gt;Had a great long chat with Evan. Went over so many topics including, Generative UI, Full Self Driving, Expo router, how to make cool demos, Apple and how they are good at marketing but not so good at execution. We talked about the difficulties in the distribution of native applications, multiple versions, backward compatibility, major version policy, rollbacks and forward. He also mentioned the cool features of Expo that help with the distribution of Native Apps.&lt;/p&gt;

&lt;p&gt;We also discussed his demo of React Server Components generated by Ai and streamed to React Native, the future after the Demo what it means for &lt;strong&gt;Server Driven UI, personalisation and the usefulness of generative UI beyond cool demos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1801630044030111889-484" src="https://platform.twitter.com/embed/Tweet.html?id=1801630044030111889"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1801630044030111889-484');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1801630044030111889&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Brooks Lybrand, Devrel React Router (Remix)
&lt;/h2&gt;

&lt;p&gt;I watched Brooks talk about bringing React Router and the newer features of React to existing CRA and Webpack apps. React Router v7 and the migration path look very promising and it aligns with the architecture I have been working on which will make it so simple to eventually upgrade to React 19. My only complaint was he didn't have any spare Limited Edition Remix hoodies to give away. I also helped him design the new React Router Remix logo but Ryan got very mad at us 😂&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1801624394466476066-342" src="https://platform.twitter.com/embed/Tweet.html?id=1801624394466476066"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1801624394466476066-342');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1801624394466476066&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Juri S, Developer Relations, NX
&lt;/h2&gt;

&lt;p&gt;Juri and I talked about how we are using the NX monorepo at Postman and how it would be a good idea to showcase the architecture and improvements we made to NX for Micro-Frontends and independent app deployments as a case study. I also gave him a demo of the breaking change detection system that my colleague Patrick and our team created to suggest semantic versioning package versions and that it would be awesome to include it in the NX release command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mo Khazali
&lt;/h2&gt;

&lt;p&gt;I discussed with Mo and Evan about bringing Module Federation to React Native, Evan is not that keen since it is an “organisational issue” not a user-facing or a DX issue but it was interesting to show our perspective on where it could be useful to send runtime modules over the air for native apps when multiple teams are deploying different parts of a large React Native Application. After our chat I found out that Module Federation is already supported in React Native if you use &lt;a href="https://re-pack.dev/docs/module-federation" rel="noopener noreferrer"&gt;Re-Pack&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Una Kravets
&lt;/h2&gt;

&lt;p&gt;It was great to meet Una in person finally. I watched her talk at C3 DevFest and even though I had seen the ThunderCats intro before it was still really funny. I hope she doesn't remember my name because I am in trouble with the Google Developer Experts program for not reporting my engagements... oops 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  Special Mentions
&lt;/h2&gt;

&lt;p&gt;These are just summaries of what I remember, some conversations went off for hours sometimes. I should have a hidden microphone and release these chats as a podcast series.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://x.com/nialljoemaher" rel="noopener noreferrer"&gt;Niall Maher&lt;/a&gt;, Carolina, &lt;a href="https://x.com/codeSTACKr" rel="noopener noreferrer"&gt;Jesse Hall&lt;/a&gt; and the Irish mafia for putting up with me for 3 days straight. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/danieljcafonso" rel="noopener noreferrer"&gt;Daniel A(l)fonso&lt;/a&gt;, &lt;a href="https://x.com/AtilaFassina" rel="noopener noreferrer"&gt;Atila&lt;/a&gt;, Mi Parcero &lt;a href="https://x.com/erickwendel_" rel="noopener noreferrer"&gt;Erick Wendel&lt;/a&gt; and &lt;a href="https://x.com/DavidKPiano" rel="noopener noreferrer"&gt;David K&lt;/a&gt; for hanging out with me as the infiltrated non-speaker and for giving me free food truck tokens.&lt;/p&gt;

&lt;p&gt;Finally thanks &lt;a href="https://x.com/JoshuaKGoldberg" rel="noopener noreferrer"&gt;Josh Goldberg&lt;/a&gt;, for being a great friend and listening to me yapping until 2 AM. We didn't talk about tech at all but we talked about so many things! Next time he's going to convince me TypeScript is not considered self-harm at this point 😂&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1802131541687927171-753" src="https://platform.twitter.com/embed/Tweet.html?id=1802131541687927171"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1802131541687927171-753');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1802131541687927171&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  One last thing... The Conference!
&lt;/h2&gt;

&lt;p&gt;This is the best frontend conference, full stop. I have been going to GitNation events for a while and it is always a pleasure to be part of this community. Rob, Daria, Anna, Alex, Lera and the rest of the crew are amazing at what they do and they really care about the details and the community. Congratulations on another amazing event! and see you at React Advanced London.&lt;/p&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%2Fjfed9wl6ksiivrchs28i.jpeg" 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%2Fjfed9wl6ksiivrchs28i.jpeg" alt="Arriving at the conf by bike" width="240" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>community</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building a Loading Component with ChatGPT</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Sat, 28 Jan 2023 00:27:04 +0000</pubDate>
      <link>https://dev.to/infoxicator/building-a-loading-component-with-chatgpt-26od</link>
      <guid>https://dev.to/infoxicator/building-a-loading-component-with-chatgpt-26od</guid>
      <description>&lt;p&gt;Have you ever seen the red progress bar on Youtube or the blue progress bar on GitHub when navigating from one page to the next?&lt;/p&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%2Fjgkhqkdq314gqgqlbjla.gif" 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%2Fjgkhqkdq314gqgqlbjla.gif" alt="youtube loading bar" width="1424" height="56"&gt;&lt;/a&gt;&lt;/p&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%2Fo8xq8nxwdmx86v4hy9n0.gif" 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%2Fo8xq8nxwdmx86v4hy9n0.gif" alt="github progress bar" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I like that little component; it's elegant and subtle and perfectly fits with Remix and React Router transitions.&lt;/p&gt;

&lt;p&gt;So I decided to build my own&lt;/p&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%2F90g1l5od3ieh73lylmeu.jpeg" 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%2F90g1l5od3ieh73lylmeu.jpeg" alt="That was easy" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I am really lazy! Can ChatGPT write for me?&lt;/li&gt;
&lt;li&gt;I want to use React, no vanilla JS&lt;/li&gt;
&lt;li&gt;No external dependencies (duh)&lt;/li&gt;
&lt;li&gt;It needs to support animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here we go! 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT first attempt:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prompt:
&lt;/h3&gt;

&lt;p&gt;"Write a react component that creates a red bar 3px in height with a dynamic width that starts at 0% and start incrementing every 10ms and dissapears when it reaches 100% width with a fade out animation. Reply with code blocks"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ProgressBar.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="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;intervalId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevWidth&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prevWidth&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="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`progress-bar &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fade-out&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="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&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="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ProgressBar&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 css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.progress-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.fade-out&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fade-out&lt;/span&gt; &lt;span class="m"&gt;0.5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;fade-out&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was a good start!&lt;/p&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%2Fz7noe4gnj4cgb4h4msj1.gif" 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%2Fz7noe4gnj4cgb4h4msj1.gif" alt="loader" width="1491" height="28"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
   Next Prompt:
&lt;/h3&gt;

&lt;p&gt;"Now, I want the progress bar to conditional render and mount an unmount when a condition is true or false, when it is unmounted it should fill the width to 100% and then fade out and disappear."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProgressBar2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isAnimating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsAnimating&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="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;let&lt;/span&gt; &lt;span class="nx"&gt;intervalId&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;show&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAnimating&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setIsAnimating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;intervalId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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;setWidth&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prevWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prevWidth&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="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAnimating&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setIsAnimating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;show&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`progress-bar &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fade-out&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="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&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="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;left&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="p"&gt;}}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="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;Prompt:&lt;br&gt;
"What? That doesn't work at all! the bar never appears!"&lt;/p&gt;

&lt;p&gt;ChatGPT:&lt;br&gt;
I apologize for the confusion, it looks like my previous response did not include the necessary CSS for the animation.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;
&lt;h3&gt;
  
  
  What CSS? That code doesn't even make sense!
&lt;/h3&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Many prompts later... an increasing list of &lt;code&gt;useEffect&lt;/code&gt; functions, and a  very apologetic ChatGPT, I decided to give up and build it myself.&lt;/p&gt;

&lt;p&gt;Boy, that was a big mistake!&lt;/p&gt;
&lt;h2&gt;
  
  
  Random trickle:
&lt;/h2&gt;

&lt;p&gt;The width increments were gradual every 10ms, but I want to add some randomness to the bar progress to give the impression that some non-deterministic async operation is actually happening... (Seriously, the bar is an illusion! It doesn't have anything to do with how long the operation takes 😂)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProgressBarProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progressSpeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&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;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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;setWidth&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;prevValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;prevValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prevValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prevValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;progressSpeed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;progressSpeed&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 was easy wasn't it?&lt;/p&gt;

&lt;h2&gt;
  
  
   React Animations
&lt;/h2&gt;

&lt;p&gt;You know what's not easy? Animations in React&lt;/p&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%2Fcbj75pfk5a7qrdn5i3p9.jpeg" 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%2Fcbj75pfk5a7qrdn5i3p9.jpeg" alt="Animations in React " width="515" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It turns out that Animations in React are a pain!&lt;/p&gt;

&lt;p&gt;When the component is unmounted, I want to fill the bar to 100% then show a fade animation. &lt;/p&gt;

&lt;p&gt;In good old JQuery is a one-liner, as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#progress-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fadeOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, but not React; React wants you to suffer! (unless you use a library like &lt;a href="https://github.com/reactjs/react-transition-group/tree/v1-stable" rel="noopener noreferrer"&gt;react-transition-group&lt;/a&gt;, of course.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the rules are the rules, and I said no libraries...&lt;/strong&gt; 😅&lt;/p&gt;

&lt;p&gt;So after consulting with ChatGPT's arch enemy and more reliable predecessor, StackOverflow. I found that you can wrap your component in a container and use a method called &lt;a href="https://reactjs.org/docs/events.html#animation-events" rel="noopener noreferrer"&gt;onAnimationEnd&lt;/a&gt; to detect when the animation finishes and then hide the element.&lt;/p&gt;

&lt;p&gt;So here is the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./progress-bar.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ProgressBarProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;show&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProgressBarProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;shouldRender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;show&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;onAnimationEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;shouldRender&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
        &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-bar-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
          &lt;span class="na"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fadeOut 0.5s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="nx"&gt;onAnimationEnd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onAnimationEnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Bar&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProgressBarProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progressSpeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&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;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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;setWidth&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;prevValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;prevValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prevValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prevValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;progressSpeed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;progressSpeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
      &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2147483647&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FF0000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;width&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="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;90&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="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`width &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;progressSpeed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;translate3d(0, 0, 0)&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It works!&lt;/strong&gt;&lt;/p&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%2Fn6bnma0cvg22cwhb8g8g.gif" 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%2Fn6bnma0cvg22cwhb8g8g.gif" alt="loading bar" width="2211" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Except it doesn't! There is an annoying "flashing glitch" when the component unmounts that I couldn't be bothered finding out why (I am lying; I spent way too long trying but then gave up)&lt;/p&gt;

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

&lt;p&gt;Use &lt;a href="https://www.npmjs.com/package/nprogress" rel="noopener noreferrer"&gt;nProgress&lt;/a&gt; 😂&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
   Real Conclusion:
&lt;/h2&gt;

&lt;p&gt;This was a fun exercise to stretch the limits of ChatGPT and how far it can go in creating entire components and UI. It did very well with the basics, but when you start adding more complex interactions, it just goes round and round in circles spitting out incoherent and repetitive code. (not without apologising, of course 😅)&lt;/p&gt;

&lt;p&gt;Worth noting that I couldn't make the component work 100% either!, mainly due to lack of time (or lack of React expertise 😂).&lt;/p&gt;

&lt;p&gt;If anyone wants to judge my midnight coding and fix the bug, and let me know why the glitch happens, here is the link to the code:&lt;br&gt;
&lt;a href="https://stackblitz.com/edit/github-9eintn-ru5xtl?file=src%2Fprogress-bar.tsx" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/github-9eintn-ru5xtl?file=src%2Fprogress-bar.tsx&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
   My Conclusion:
&lt;/h2&gt;

&lt;p&gt;I need to get better at React animations.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why React Router is excellent for Micro-Frontends</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Sat, 21 Jan 2023 10:29:27 +0000</pubDate>
      <link>https://dev.to/infoxicator/why-react-router-is-excellent-for-micro-frontends-2a21</link>
      <guid>https://dev.to/infoxicator/why-react-router-is-excellent-for-micro-frontends-2a21</guid>
      <description>&lt;p&gt;As you probably noticed, I am a huge fan of React Router 6!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is Remix for Single Page Applications&lt;/strong&gt; and gives your apps "super powers" while taking you closer to the platform.&lt;/p&gt;

&lt;p&gt;You also know that I love working with large frontend applications, and my passion is frontend architecture at scale, especially Micro-Frontends. So, &lt;strong&gt;where is the intersection between these two technologies?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  React Router 3
&lt;/h2&gt;

&lt;p&gt;My journey with React Router and Micro-Frontends started at American Express, working as one of the maintainers of &lt;a href="https://github.com/americanexpress/one-app" rel="noopener noreferrer"&gt;One App&lt;/a&gt;, a server-side rendered React Micro-Frontend framework (That's a mouthful!) &lt;/p&gt;

&lt;p&gt;At the core of this framework was React Router 3. It was so powerful, versatile, and one of the best-designed libraries for React. (At over 1 Billion downloads, the team behind it must have done something right 👏)&lt;/p&gt;

&lt;p&gt;React Router 4/5 was released with improved support for hooks. Still, we could not upgrade because of missing features from v3, like the ability to pull the entire route tree programmatically using &lt;code&gt;childRoutes&lt;/code&gt; so we created and maintained a &lt;a href="https://github.com/americanexpress/one-app-router" rel="noopener noreferrer"&gt;fork&lt;/a&gt; of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then React Router 6 came along&lt;/strong&gt; 🎉, bringing back most of the great concepts from v3 and doubling down on one of its killer features, Nested Routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested Routing
&lt;/h2&gt;

&lt;p&gt;First, what's Nested Routing? An image speaks volumes, so take a look at this:&lt;br&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%2F94303ogmtis3e65ddjwq.gif" 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%2F94303ogmtis3e65ddjwq.gif" alt="Nested Routing Demo" width="685" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I haven't found a better explanation than this visual demo from the Remix page, but in &lt;a href="https://twitter.com/mjackson/" rel="noopener noreferrer"&gt;Michael Jackson's&lt;/a&gt; &lt;a href="https://podrocket.logrocket.com/remix-react-router-repeat" rel="noopener noreferrer"&gt;words&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It's a way to split a webpage into more granular pieces. Those pieces match a segment of the URL. When you have such granularity, you can perform lots of really interesting optimisations and enable workflows that are difficult when you don't have that concept."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hold on a second, "Split a webpage into more granular pieces."  &lt;strong&gt;That's precisely what Micro-Frontends are about!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Combined with techniques like "Runtime Composition", those smaller pieces can be developed, tested and deployed independently by separate teams. React Router nested routing will take you halfway there, helping you split your application in a predictable and performant way. &lt;/p&gt;

&lt;h2&gt;
  
  
  Decoupling
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"For a system to be distributed, it has to be decoupled first."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React Router builds on top of Nested Routing to separate your application's data loading, error handling and rendering. &lt;/p&gt;

&lt;p&gt;In a Micro-Frontend architecture, it is essential that your UI is composable, encapsulated and, most importantly, decoupled. &lt;/p&gt;

&lt;h2&gt;
  
  
  Data Loaders
&lt;/h2&gt;

&lt;p&gt;Separating the data loading from the rendering is a fantastic decision for performance (Fetch before you render) and parallel fetching, but this also has a nice side effect on composability and separation of concerns.&lt;/p&gt;

&lt;p&gt;One of my &lt;a href="https://dev.to/infoxicator/rules-of-micro-frontends-3njg"&gt;"Rules for Micro-Frontends"&lt;/a&gt; is that each Micro-Frontends should load its own data. Having every route segment matched to a loader ensures that your data dependencies are explicit and removes indirection.&lt;/p&gt;

&lt;p&gt;This makes ownership, refactoring and flexibility features that are baked-in into your architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  State
&lt;/h2&gt;

&lt;p&gt;Another of my recommendations is to avoid sharing state to prevent "accidental coupling". This will keep your application from becoming a "distributed monolith", and you will get most of the benefits of a Micro-Frontend architecture.&lt;/p&gt;

&lt;p&gt;React Router 6 loaders and actions remove most of the "global state" needs and greatly simplify React Applications. The URL becomes the "global state" and keeps things decoupled while ensuring all the different parts of your UI are in sync while relying on the web platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrations
&lt;/h2&gt;

&lt;p&gt;React Router and Micro-Frontends are great for migrations! (I need to write a separate blog post on this section alone) but in summary, the flexibility and route-matching algorithm of React Router enables you to migrate legacy applications piece by piece and do a "Reverse Strangler Pattern" migration.&lt;/p&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%2Fwtaozwzyxxpgn3nnt3pz.jpeg" 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%2Fwtaozwzyxxpgn3nnt3pz.jpeg" alt="Reverse strangler diagram" width="785" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Remix?
&lt;/h2&gt;

&lt;p&gt;Remix shares most of the best traits of React Router, and I recommend it 100% for new applications. It removes a lot of the pain points that tend to send people down the Micro-Frontends path; however, Micro-Frontends are meant to solve an organisational scaling problem, so at some point, for large organisations it would be great to have independent deployability of segments of a Remix application that are owned by an entire team.&lt;/p&gt;

&lt;p&gt;The technical problem is runtime composition. It is possible, it has been done before by Jacob from Remix and my friend &lt;a href="https://portal.gitnation.org/contents/scaling-up-with-remix-and-micro-frontends" rel="noopener noreferrer"&gt;Adrien&lt;/a&gt;, but for a Framework like Remix, there are loads of nuances that need to be addressed to get the benefits of independent deployability. &lt;/p&gt;

&lt;p&gt;TLDR; it is possible, but someone needs to sit down and design a good playbook around it and tweak the tech to support it. (don't look at me!)&lt;/p&gt;

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

&lt;p&gt;React Router is a fantastic tool! Add independent deployments and runtime composition on top of it, and you end up with a robust Micro-Frontend architecture that can help you scale your teams and solve your organisational scaling problems.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Modals with React Router 6 and Remix</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Thu, 19 Jan 2023 23:53:59 +0000</pubDate>
      <link>https://dev.to/infoxicator/modals-with-react-router-6-and-remix-1e35</link>
      <guid>https://dev.to/infoxicator/modals-with-react-router-6-and-remix-1e35</guid>
      <description>&lt;p&gt;Popularized by Pinterest, Instagram, and others in the 2010s. Modals are typically used as a kind of "detail" view to focus on a particular object in a collection (like a Pinterest board) while not taking you completely out of the context of the parent page.&lt;/p&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%2Fhte39ajqtx4ryjmftnss.gif" 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%2Fhte39ajqtx4ryjmftnss.gif" alt="Modal Gif" width="600" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is &lt;a href="https://github.com/remix-run/react-router/tree/main/examples/modal" rel="noopener noreferrer"&gt;an example on the React Router 6 Repository&lt;/a&gt; on how to implement this pattern by using &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;backgroundLocation&lt;/code&gt; to keep the parent page visible and show the modal on top.&lt;/p&gt;

&lt;p&gt;How do you achieve the same results with React Router Data Routers (^6.4.0) and Remix? Let's find out! &lt;/p&gt;

&lt;p&gt;First, when using &lt;code&gt;loaders&lt;/code&gt; we will be unable to use the same mechanism of setting a &lt;code&gt;state.backgroundLocation&lt;/code&gt; because the route state doesn't exist at the point where we define our routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We can't access state here!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&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;&lt;a href="https://stackblitz.com/edit/github-9lelz2-tdcovz?file=src/main.tsx" rel="noopener noreferrer"&gt;Source Code of this attempt here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So there are two options for Modals with Data Loaders, both of them have their own pros and cons depending on your use case:&lt;/p&gt;

&lt;h2&gt;
  
  
  Modals using Nested Routing
&lt;/h2&gt;

&lt;p&gt;The first option is to use React Router's nested routing capabilities to enable a modal that can be shown inside its parent route:&lt;/p&gt;

&lt;p&gt;Let's use the same example from the React Router repository. We want to show a modal whenever the user clicks on an image and then have the image detail view inside the modal instead of a new page.&lt;/p&gt;

&lt;p&gt;Routing configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&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="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ImageView&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="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gallery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Gallery&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&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="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NoMatch&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="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;code&gt;Modal&lt;/code&gt; component is now a nested route inside the &lt;code&gt;Gallery&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;In Remix just create a new file folder structure to create the nested route&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;routes&lt;/span&gt; 
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt; 
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;gallery&lt;/span&gt;   
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next step, we need to add an &lt;code&gt;Outlet&lt;/code&gt; to the &lt;code&gt;Gallery&lt;/code&gt; component to render the modal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Gallery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 24px&lt;/span&gt;&lt;span class="dl"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Gallery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;...&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Outlet&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Modals can use their own &lt;code&gt;loaders&lt;/code&gt;: If your modals need to access data fetching, they can be assigned their own loader to separate them from the parent route and work independently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It doesn't require a lot of setup: They are just nested routes that render on top of the parent route.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Persistent navigation: Because the modals are just regular routes, you can reference them, open and close them using the URL path.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cons:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Modals need to be configured under a route, which means that it's not possible to render them in the root &lt;code&gt;/&lt;/code&gt; URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modals will be displayed only inside a defined nested route and cannot be accessed from any page. If you want to show them on a different path, you need to create those routes manually over and over again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you want to move a modal, you must refactor the entire route segment that uses it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is sometimes hard to keep the context of the background page because outlets have to be in the right place.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/github-9lelz2-hefa9t?file=src/App.tsx" rel="noopener noreferrer"&gt;Link to entire code Example&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Modals Using Search Params
&lt;/h2&gt;

&lt;p&gt;An alternative to Nested Routes is to use the Search Params in the URL (Use the platform! 💪) &lt;/p&gt;

&lt;p&gt;To open a modal with the picture from the gallery, we will need to navigate to the following URL path:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;?modal-type=gallery-img&amp;amp;gallery-img-id=1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then we can access the URL Search Params in the root loader of the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoaderFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&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;queryParams&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modalType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modal-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;galleryImgId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gallery-img-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modalType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gallery-img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;galleryImgId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;must pass the gallery-img-id param if you want to render the 'gallery-img' modal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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="na"&gt;modalType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gallery-img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;galleryImgId&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="kc"&gt;null&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;Then in the render function of the root of your application, we check for the modal type and the parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modalProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ModalProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="nx"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="nx"&gt;modalProps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;modalProps&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Outlet&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="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;Inside the &lt;code&gt;Modal&lt;/code&gt; Component, we can check for the &lt;code&gt;modalType&lt;/code&gt; and render a different modal and content depending on that value. You can &lt;br&gt;
register and create as many modals as required using a simple interface and conditional rendering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Modals are global; You can use them in the root URL path &lt;code&gt;/&lt;/code&gt; and can be opened from anywhere in the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy to refactor and move around within the application.&lt;br&gt;
They also support persistent navigation and manage their visibility state using the URL.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;These modals can't have their own loaders, so they either need to request their data inside their render function (Render then Fetch) or get their data passed as props from a parent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They have to be "registered" in the root of your application using a modal rendering engine or utility which could be harder to maintain in the long term.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Search Params in the root will trigger all the other loaders, which could cause re-rendering and performance issues. A potential solution is to introduce &lt;code&gt;shouldRevalidate()&lt;/code&gt; if you don't want to re-run loaders.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/github-9lelz2-c6jqjc?file=src/App.tsx" rel="noopener noreferrer"&gt;Link to the source code&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;One pattern is not better than the other; both have advantages and disadvantages, depending on your use case. &lt;/p&gt;

&lt;p&gt;If you need your modals to have their own loaders, then use the nested routing approach but bear in mind that they are not very flexible and easy to refactor.&lt;/p&gt;

&lt;p&gt;If you want to create "global modals" that can be opened from anywhere and are more flexible, you can use the Search Params approach; however, be aware of tight coupling in the root of the application and the performance implications of not using loaders.&lt;/p&gt;

&lt;h2&gt;
  
  
   Credits:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/jonathan-higger/" rel="noopener noreferrer"&gt;Jon&lt;/a&gt;, who made a video on &lt;a href="https://www.youtube.com/watch?v=DR5Hvw96O6c" rel="noopener noreferrer"&gt;how to create Modals using Search Params&lt;/a&gt; and also a repository with the complete example &lt;a href="https://github.com/jjhiggz/remix-query-modal-example" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as usual &lt;a href="https://twitter.com/brophdawg11" rel="noopener noreferrer"&gt;Matt&lt;/a&gt; from the Remix team who's always so helpful in answering these type of questions on Discord.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Module Federation Shared API</title>
      <dc:creator>Ruben Casas</dc:creator>
      <pubDate>Tue, 03 Jan 2023 19:35:04 +0000</pubDate>
      <link>https://dev.to/infoxicator/module-federation-shared-api-ach</link>
      <guid>https://dev.to/infoxicator/module-federation-shared-api-ach</guid>
      <description>&lt;p&gt;This is one of the most potent APIs of Module Federation, yet there needs to be more documentation on how it works and how it could help with the performance of your distributed applications. &lt;/p&gt;

&lt;p&gt;In this post, we will explore every aspect of the Shared API, explaining how to use it and when it might come in handy. 🔍&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the Shared API?
&lt;/h2&gt;

&lt;p&gt;So, what is the Shared API exactly? 🤔 It's part of the plugin configuration options in Module Federation. You can pass it an array or object called &lt;code&gt;shared&lt;/code&gt;, which contains a list of dependencies that can be shared and used by other federated apps (aka "remotes").&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;remotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;exposes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Definition:
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;shared (object | [string])&lt;/code&gt;: An object or Array of strings containing a list of dependencies that can be shared and consumed by other federated apps.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eager (boolean)&lt;/code&gt;: If true, the dependency will be eagerly loaded and made available to other federated apps as soon as the host application starts. If false, the dependency will be lazily loaded when it is first requested by a federated app.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;singleton (boolean)&lt;/code&gt;: If true, the dependency will be treated as a singleton and only a single instance of it will be shared among all federated apps.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;requiredVersion (string)&lt;/code&gt;: Specifies the required version of the dependency. If a federated app tries to load an incompatible version of the dependency, two copies will be loaded. If the &lt;code&gt;singleton&lt;/code&gt; option is set to &lt;code&gt;true&lt;/code&gt; a warning will be printed in the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do you need it?
&lt;/h2&gt;

&lt;p&gt;When you have federated modules, they're bundled separately and include all the dependencies they need to function. However, when they're used in a host application, it's possible for multiple copies of the same dependency to be downloaded. This can hurt performance and make users download more JavaScript than necessary. 😰&lt;/p&gt;

&lt;p&gt;This is where the shared API becomes really handy: You can avoid downloading multiple copies of the same dependency and improve the performance of your application. 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventing Duplication
&lt;/h2&gt;

&lt;p&gt;Let's stick to the typical example of Webpack, and share and deduplicate &lt;code&gt;lodash&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Let's say you have two modules, Module A and Module B, and both of them require &lt;code&gt;lodash&lt;/code&gt; in order to function independently. &lt;/p&gt;

&lt;p&gt;But when they're used in a host application that brings both modules together, the Shared API comes into play. If there is already a preloaded, shared copy of &lt;code&gt;lodash&lt;/code&gt; available, Module A and Module B will use that copy instead of loading their own independent copies.&lt;/p&gt;

&lt;p&gt;This copy could be loaded by made available by the host or another remote application inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Tip: Both the remote and host have to add the same dependency in "shared" for it to be available for consumption.&lt;/p&gt;
&lt;/blockquote&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%2Fu4wuu9g7dqq16oi2jvtv.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%2Fu4wuu9g7dqq16oi2jvtv.png" alt="Module Federation Sharing lodash" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;If you are familiar with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import" rel="noopener noreferrer"&gt;Dynamic Imports&lt;/a&gt;, Module Federation is nearly identical; it requests a module and returns a promise that fulfils with an object containing all exports from the &lt;code&gt;moduleName&lt;/code&gt; declared in the &lt;code&gt;exposes&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Module Federation's async nature makes the &lt;code&gt;shared&lt;/code&gt; API so flexible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Async Dependency Loading
&lt;/h3&gt;

&lt;p&gt;When a module is required, it will load a file called &lt;code&gt;remoteEntry.js&lt;/code&gt; listing all the dependencies the module needs. Because this operation is asynchronous, the container can check all the &lt;code&gt;remoteEntry&lt;/code&gt; files and list all the dependencies that each module has declared in &lt;code&gt;shared&lt;/code&gt;; then, the host can load a single copy and share it with all the modules that need it.&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;shared&lt;/code&gt; relies on an asynchronous operation to be able to inspect and resolve the dependencies, if your application or module loads synchronously and it declares a dependency in &lt;code&gt;shared&lt;/code&gt;, you might encounter the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Shared&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;eager&lt;/span&gt; &lt;span class="nx"&gt;consumption&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To solve the error above, there are two options:&lt;/p&gt;

&lt;h3&gt;
  
  
  Eager Consumption
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;lodash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;eager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Individual dependencies can be marked as &lt;code&gt;eager: true&lt;/code&gt;; this option doesn't put the dependencies in an async chunk, so they can be provided synchronously; however, this means that those dependencies will always be downloaded, potentially impacting bundle size. The recommended solution is to load your module asynchronously by wrapping it into an async boundary:&lt;/p&gt;

&lt;h3&gt;
  
  
  Using an Async Boundary:
&lt;/h3&gt;

&lt;p&gt;Note: this only applies to the application's entry point; remote modules consumed via module federation are automatically wrapped in an Async Boundary.&lt;/p&gt;

&lt;p&gt;To create an async boundary, use a dynamic import to ensure your entry point runs asynchronously:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./bootstrap.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bootstrap.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&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;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
   Versioning
&lt;/h2&gt;

&lt;p&gt;So what happens if two remote modules use different versions of the same dependency? &lt;/p&gt;

&lt;p&gt;💥?&lt;/p&gt;

&lt;p&gt;Nope... everything just works™️! &lt;/p&gt;

&lt;p&gt;Versioning is probably one of the best (if not the best) features of the shared API, and it is also enabled by default 🙌&lt;/p&gt;

&lt;p&gt;If the semantic version ranges for those dependencies don't match, then Module Federation is powerful enough to identify them and provide separate copies, so you don't accidentally load the wrong version containing breaking changes. This obviously can cause issues with performance because you end up downloading different versions of a dependency, but at least your app doesn't break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Singleton Loading
&lt;/h2&gt;

&lt;p&gt;I know what you are thinking; what if I want to guarantee that only one copy of a given dependency is loaded at all times. (cough cough React, cough cough)&lt;/p&gt;

&lt;p&gt;Passing &lt;code&gt;singlton: true&lt;/code&gt; to the dependency object achieves precisely that...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^18.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&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="nl"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^18.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's something to be aware of: if one of your remote modules tries to load an incompatible dependency version that has been marked as a singleton, Webpack will print the following warning in the console:&lt;/p&gt;

&lt;p&gt;But don't panic! The build won't actually break, and Webpack will continue to bundle and load your applications. However, the warning is there to let you know that there may be some unexpected behaviour due to compatibility issues. So, if you see this warning, it's a good idea to go and align your dependencies to avoid any problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downsides / Trade-Offs
&lt;/h2&gt;

&lt;p&gt;Everything is a trade-off, but you can only accept and mitigate them if you are aware they exist. Here are some issues that you might encounter using the Shared API:&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies mismatches at Runtime
&lt;/h3&gt;

&lt;p&gt;Because the applications are built at a different point in time by another Webpack process, they don't share the same dependency graph, and we can only rely on Semantic Versioning ranges to deduplicate and provide the same dependency version. &lt;/p&gt;

&lt;p&gt;There could be a case where you have built and tested your remote with version &lt;code&gt;1.0.0&lt;/code&gt; of a library, but then when loaded by the host, because the Semantic Versioning Range &lt;code&gt;^1.0.0&lt;/code&gt; satisfies &lt;code&gt;1.1.0&lt;/code&gt;, you might end up loading &lt;code&gt;1.1.0&lt;/code&gt; at Runtime in production that might or might not have compatibility issues.&lt;/p&gt;

&lt;p&gt;This can be mitigated by aligning versions as much as possible (Using a monorepo with a single package JSON might help).&lt;/p&gt;

&lt;p&gt;This downside concerns our trust in Semantic Versioning ranges rather than Module Federation and the Shared API. In Distributed Systems (similar to Microservices), a contract is required to guarantee the stability and reliability of the system. In the case of the Shared API, the Semantic Version Range is the contract (potentially not a good one).&lt;/p&gt;

&lt;p&gt;From my experience, there isn't a better alternative to shared dependencies on a distributed frontend application, and even though the Share API is not perfect, it is the best we have right now.&lt;/p&gt;

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

&lt;p&gt;In conclusion, the Module Federation Shared API is a powerful tool for improving the performance of distributed applications. It allows you to share dependencies between modules and avoid unnecessary duplication, resulting in faster load times and better overall performance&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webpack</category>
      <category>performance</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
