<?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: Adimchi Oka</title>
    <description>The latest articles on DEV Community by Adimchi Oka (@vectorchi).</description>
    <link>https://dev.to/vectorchi</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4197828%2Febdf8ad6-1412-4e22-b348-bf27a50a870b.png</url>
      <title>DEV Community: Adimchi Oka</title>
      <link>https://dev.to/vectorchi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vectorchi"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Adimchi Oka</dc:creator>
      <pubDate>Wed, 21 Oct 2026 08:41:10 +0000</pubDate>
      <link>https://dev.to/vectorchi/-26cn</link>
      <guid>https://dev.to/vectorchi/-26cn</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i" class="crayons-story__hidden-navigation-link"&gt;Better Loading States for AI Apps&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vectorchi" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4197828%2Febdf8ad6-1412-4e22-b348-bf27a50a870b.png" alt="vectorchi profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vectorchi" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Adimchi Oka
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Adimchi Oka
                
                
              
              &lt;div id="story-author-preview-content-4888093" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vectorchi" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4197828%2Febdf8ad6-1412-4e22-b348-bf27a50a870b.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Adimchi Oka&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i" id="article-link-4888093"&gt;
          Better Loading States for AI Apps
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/react"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;react&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Better Loading States for AI Apps</title>
      <dc:creator>Adimchi Oka</dc:creator>
      <pubDate>Wed, 21 Oct 2026 08:40:03 +0000</pubDate>
      <link>https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i</link>
      <guid>https://dev.to/vectorchi/better-loading-states-for-ai-apps-3f0i</guid>
      <description>&lt;p&gt;You click "Generate". The model starts working, and the UI shows a spinning circle. Ten seconds later it's still spinning. At thirty seconds you start to wonder whether the request died. The spinner can't tell you either way.&lt;/p&gt;

&lt;p&gt;The idea came from Claude Code. While it works, it doesn't show a bare spinner: it shows playful, ever-changing status words like "Pontificating…", and I noticed I actually &lt;em&gt;read&lt;/em&gt; them. The wait felt shorter, and the tool felt more alive. Most apps I use, including ones with AI features, still show the same silent spinner.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;fun-loader&lt;/strong&gt;: a small web component that brings that idea to any web app, filling the wait with short, rotating messages like "Connecting the dots…", "Compiling thoughts…" or "Crunching the numbers…".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0q53v1x148zji81uswxg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0q53v1x148zji81uswxg.gif" alt="Four loaders rotating their messages, each with a different theme and icon" width="759" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's about 3 kB minified and compressed, has no dependencies, works as a standard custom element, and comes with React and Vue components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother?
&lt;/h2&gt;

&lt;p&gt;Loading states are the quiet part of a product. They're usually an afterthought: add a spinner, ship it, move on. But when a wait runs past a few seconds, as it often does with LLMs, a spinner becomes dead air, and people start to doubt that anything is happening.&lt;/p&gt;

&lt;p&gt;A short rotating message helps in three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It shows that work is still going on.&lt;/li&gt;
&lt;li&gt;It gives people something light to read instead of watching the clock.&lt;/li&gt;
&lt;li&gt;It makes the product feel considered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use it (and when not to)
&lt;/h2&gt;

&lt;p&gt;fun-loader is for open-ended waits that usually take a few seconds or more: an LLM response, image generation, a report, a payment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For waits under a second,&lt;/strong&gt; a plain spinner is fine. Rotating text would only flicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When you know the actual progress,&lt;/strong&gt; such as a file upload, use a progress bar. It's more honest than any message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;With a bundler (Vite, webpack, Next.js and so on):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @vectorchi/fun-loader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vectorchi/fun-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// registers &amp;lt;fun-loader&amp;gt; and every theme&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with no build step, from a CDN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@vectorchi/fun-loader@0.3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic usage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Plain HTML&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;fun-loader&lt;/span&gt; &lt;span class="na"&gt;theme=&lt;/span&gt;&lt;span class="s"&gt;"thinking"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/fun-loader&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;React / Next.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FunLoader&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;@vectorchi/fun-loader/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FunLoader&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"art"&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sparkle"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works in React 18 and 19, and in Next.js you can render it straight from a server component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vue&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FunLoader&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;@vectorchi/fun-loader/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;FunLoader&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt; &lt;span class="na"&gt;theme=&lt;/span&gt;&lt;span class="s"&gt;"dev"&lt;/span&gt; &lt;span class="na"&gt;shuffle&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Show it while you wait, and remove it when the result arrives. Removing it also stops its timer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Themes and options
&lt;/h2&gt;

&lt;p&gt;There are eight built-in themes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thinking&lt;/code&gt; (default): chat and general AI answers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dev&lt;/code&gt;: code generation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;design&lt;/code&gt;: design and layout tools&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;art&lt;/code&gt;: image generation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;finance&lt;/code&gt;: reports and dashboards&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transfer&lt;/code&gt;: payments and money transfers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kitchen&lt;/code&gt;: anything playful&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;calm&lt;/code&gt;: long waits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pass your own messages,&lt;/li&gt;
&lt;li&gt;pick one of five animated icons, or none,&lt;/li&gt;
&lt;li&gt;shuffle the order or change the pace,&lt;/li&gt;
&lt;li&gt;style it with CSS custom properties and &lt;code&gt;::part()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accessibility is built in. Screen readers hear one short label ("Loading" by default) instead of every rotating phrase, and with reduced motion turned on, the movement is replaced by a gentle fade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it helps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI and LLM interfaces&lt;/strong&gt;&lt;br&gt;
This is the obvious one: show something better than a blank spinner until the first token arrives. For streamed responses, remove the loader when the first chunk lands, because from then on the growing text is the progress indicator. The docs include a short guide for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payment and transfer flows&lt;/strong&gt;&lt;br&gt;
People are anxious while their money is moving, so the &lt;code&gt;transfer&lt;/code&gt; theme is deliberately calm: "Checking the details…", "This can take a few seconds…", "Almost there…". It also never claims a specific step, such as "Verifying recipient", because the loader can't know which step your backend is really on, and in a payment flow a message like that would be misleading. If your flow has real stages, pass your own messages that match them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reports and data processing&lt;/strong&gt;&lt;br&gt;
Anything that reliably takes a few seconds benefits from a little context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal tools&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;design&lt;/code&gt; themes work well in admin dashboards and ops tools, where people look at loading states all day.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few design notes
&lt;/h2&gt;

&lt;p&gt;I kept the surface area small on purpose. There's no animation library and no configuration object, just a custom element that does one job.&lt;/p&gt;

&lt;p&gt;The messages are short and a little informal. They shouldn't read like marketing copy, more like a product chatting quietly while it works. And when the built-in themes don't fit, you can pass your own list and the component stays out of the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docs and live playground:&lt;/strong&gt; &lt;a href="https://vectorchi.github.io/fun-loader?ref=devto" rel="noopener noreferrer"&gt;vectorchi.github.io/fun-loader&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/VectorChi/fun-loader" rel="noopener noreferrer"&gt;github.com/VectorChi/fun-loader&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/@vectorchi/fun-loader" rel="noopener noreferrer"&gt;@vectorchi/fun-loader&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've been shipping the same spinner for years, this is a small upgrade: swap it in, pick a theme that fits the moment, and give people something better to look at while they wait.&lt;/p&gt;

&lt;p&gt;What does your app show while the model is thinking? I'd love to hear what's worked for you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
