<?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: Harsh Dubey</title>
    <description>The latest articles on DEV Community by Harsh Dubey (@hardy30894).</description>
    <link>https://dev.to/hardy30894</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%2F3859927%2Fd13f53e3-42f2-4394-8f82-e1cb8ffdf117.png</url>
      <title>DEV Community: Harsh Dubey</title>
      <link>https://dev.to/hardy30894</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hardy30894"/>
    <language>en</language>
    <item>
      <title>How we built a terminal UI framework that only repaints what changed.</title>
      <dc:creator>Harsh Dubey</dc:creator>
      <pubDate>Fri, 03 Apr 2026 18:33:32 +0000</pubDate>
      <link>https://dev.to/hardy30894/how-we-built-a-terminal-ui-framework-that-only-repaints-what-changed-359a</link>
      <guid>https://dev.to/hardy30894/how-we-built-a-terminal-ui-framework-that-only-repaints-what-changed-359a</guid>
      <description>&lt;p&gt;Every terminal framework we tried repaints the entire screen every frame. Write a character, &lt;em&gt;repaint 10,000 cells.&lt;/em&gt; Scroll one line, repaint 10,000 cells.&lt;/p&gt;

&lt;p&gt;We decided to treat the terminal like a display server instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;What if we tracked every cell in a typed-array buffer and only wrote the ones that actually changed?&lt;/p&gt;

&lt;p&gt;That's Storm. A React-based terminal UI framework where the renderer diffs individual cells between frames. On a typical scroll frame, 97% of cells are unchanged — Storm skips them entirely.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Typed-array buffers&lt;/strong&gt; — &lt;code&gt;Int32Array&lt;/code&gt; + &lt;code&gt;Uint8Array&lt;/code&gt; instead of JS objects. A 200×50 terminal has 10,000 cells. Traditional frameworks create 10,000 objects per frame. Storm creates zero — the buffer is flat arrays. ~90% less GC pressure.&lt;br&gt;
&lt;strong&gt;Cell-level diff&lt;/strong&gt; — After painting into the buffer, the diff engine compares frame N against frame N+1 cell by cell. Only changed cells produce ANSI output. The rest are skipped entirely.&lt;br&gt;
&lt;strong&gt;WASM acceleration&lt;/strong&gt; — An optional 35KB Rust module handles ANSI string generation 3.4× faster. Loads automatically, falls back to TypeScript silently.&lt;br&gt;
&lt;strong&gt;Dual-speed rendering&lt;/strong&gt; — React handles structural changes (adding/removing components). For animation and scrolling, &lt;code&gt;requestRender()&lt;/code&gt; writes directly to the buffer — no React reconciliation, no layout rebuild. 0.5ms per frame vs 5ms.                                                          &lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the box
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;98 components&lt;/strong&gt; - from Box and Text to CommandPalette, TextArea, Markdown, DatePicker - 19 AI widgets — OperationTree, MessageBubble, ApprovalPrompt, StreamingText, SyntaxHighlight&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;85 hooks&lt;/strong&gt; — including 15 headless behavior hooks- Built-in DevTools, render heatmap, accessibility audit, time-travel, inspector
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH serving&lt;/strong&gt; — serve your TUI over SSH with auth and rate limiting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&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; @orchetron/storm react
&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="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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                                                                                                            
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Spinner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useTui&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@orchetron/storm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                                                      

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                                                                                                                      
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTui&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                                                                                                                          
    &lt;span class="nf"&gt;useInput&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;exit&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;Box&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;                                                                                                                                 
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Spinner&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diamond&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#82AAFF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;                                                                                                      
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;bold&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#82AAFF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;storm&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;alive&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&amp;gt;                                                                                              &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Box&amp;gt;                                                                                                                                           &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;                                                                                                                                                  
  &lt;span class="p"&gt;}&lt;/span&gt;                                                                                                                                                     
  &lt;span class="nf"&gt;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="nf"&gt;waitUntilExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10 lines. Running TUI with animated spinner and keyboard input.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The numbers&lt;/strong&gt;   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full frame: 2.3ms (7× headroom above 60fps)&lt;/li&gt;
&lt;li&gt;Cell skip rate: 97%&lt;/li&gt;
&lt;li&gt;WASM diff: 3.4× faster&lt;/li&gt;
&lt;li&gt;DECSTBM scroll: 78% fewer bytes to stdout&lt;/li&gt;
&lt;li&gt;SyntaxHighlight: 500K lines virtualized in 2.7s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number is real — no cache tricks, no theoretical peaks. Run them yourself: &lt;code&gt;npx tsx examples/benchmarks.ts&lt;/code&gt;                                       &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="//github.com/orchetron/storm"&gt;github.com/orchetron/storm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Website: &lt;a href="//storm.orchetron.com"&gt;storm.orchetron.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;npm install @orchetron/storm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Benchmarks: &lt;a href="//storm.orchetron.com/benchmarks"&gt;storm.orchetron.com/benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Storm is MIT licensed. We'd love feedback — what would you build with it?&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
