<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[UI Dev]]></title><description><![CDATA[UI Dev]]></description><link>https://uidev.net</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 03:57:46 GMT</lastBuildDate><atom:link href="https://uidev.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[JavaScript Type Conversions]]></title><description><![CDATA[Type conversion (also known as type coercion) is one of JavaScript's most powerful yet often misunderstood features. Whether you're converting strings to numbers, booleans to integers, or arrays to strings, understanding these conversions will make y...]]></description><link>https://uidev.net/javascript-type-conversions</link><guid isPermaLink="true">https://uidev.net/javascript-type-conversions</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Sun, 08 Feb 2026 01:02:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769896899387/177210c7-95c7-4fc8-b847-99c4179556a5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Type conversion (also known as type coercion) is one of JavaScript's most powerful yet often misunderstood features. Whether you're converting strings to numbers, booleans to integers, or arrays to strings, understanding these conversions will make you a more effective JavaScript developer.</p>
<h2 id="heading-string-to-number-conversions">String to Number Conversions</h2>
<h3 id="heading-the-unary-plus-operator">The Unary Plus Operator (+)</h3>
<p>The fastest and most concise way to convert a string to a number:</p>
<pre><code class="lang-javascript">+<span class="hljs-string">"42"</span>           <span class="hljs-comment">// 42</span>
+<span class="hljs-string">"3.14"</span>         <span class="hljs-comment">// 3.14</span>
+<span class="hljs-string">"-10"</span>          <span class="hljs-comment">// -10</span>
+<span class="hljs-string">"0xFF"</span>         <span class="hljs-comment">// 255 (hex notation works!)</span>
</code></pre>
<p><strong>Watch out for edge cases:</strong></p>
<pre><code class="lang-javascript">+<span class="hljs-string">"123abc"</span>       <span class="hljs-comment">// NaN</span>
+<span class="hljs-string">""</span>             <span class="hljs-comment">// 0 (empty string becomes 0)</span>
+<span class="hljs-string">" "</span>            <span class="hljs-comment">// 0 (whitespace becomes 0)</span>
</code></pre>
<h3 id="heading-the-double-bitwise-not">The Double Bitwise NOT (~~)</h3>
<p>Great for converting to integers, as it truncates decimals:</p>
<pre><code class="lang-javascript">~~<span class="hljs-string">"42"</span>          <span class="hljs-comment">// 42</span>
~~<span class="hljs-string">"3.14"</span>        <span class="hljs-comment">// 3 (truncates decimal)</span>
~~<span class="hljs-string">"-10"</span>         <span class="hljs-comment">// -10</span>
</code></pre>
<p><strong>Important difference:</strong></p>
<pre><code class="lang-javascript">+<span class="hljs-string">"123abc"</span>       <span class="hljs-comment">// NaN</span>
~~<span class="hljs-string">"123abc"</span>      <span class="hljs-comment">// 0 (returns 0 instead of NaN)</span>
</code></pre>
<h3 id="heading-number-constructor">Number() Constructor</h3>
<p>More explicit and readable:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">Number</span>(<span class="hljs-string">"42"</span>)         <span class="hljs-comment">// 42</span>
<span class="hljs-built_in">Number</span>(<span class="hljs-string">"3.14"</span>)       <span class="hljs-comment">// 3.14</span>
<span class="hljs-built_in">Number</span>(<span class="hljs-string">""</span>)           <span class="hljs-comment">// 0</span>
<span class="hljs-built_in">Number</span>(<span class="hljs-string">"abc"</span>)        <span class="hljs-comment">// NaN</span>
</code></pre>
<h3 id="heading-parseint-and-parsefloat">parseInt() and parseFloat()</h3>
<p>The classic methods, especially useful for strings with mixed content:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"42"</span>)       <span class="hljs-comment">// 42</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"42px"</span>)     <span class="hljs-comment">// 42 (stops at first non-digit)</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"3.14"</span>)     <span class="hljs-comment">// 3 (always returns integer)</span>
<span class="hljs-built_in">parseFloat</span>(<span class="hljs-string">"3.14"</span>)   <span class="hljs-comment">// 3.14</span>
<span class="hljs-built_in">parseFloat</span>(<span class="hljs-string">"3.14px"</span>) <span class="hljs-comment">// 3.14</span>
</code></pre>
<p><strong>Pro tip:</strong> Always specify the radix with parseInt:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"10"</span>, <span class="hljs-number">10</span>)   <span class="hljs-comment">// 10 (decimal)</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"10"</span>, <span class="hljs-number">2</span>)    <span class="hljs-comment">// 2 (binary)</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"FF"</span>, <span class="hljs-number">16</span>)   <span class="hljs-comment">// 255 (hexadecimal)</span>
</code></pre>
<h3 id="heading-arithmetic-tricks">Arithmetic Tricks</h3>
<p>Multiplication and subtraction force type conversion:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"42"</span> * <span class="hljs-number">1</span>             <span class="hljs-comment">// 42</span>
<span class="hljs-string">"3.14"</span> * <span class="hljs-number">1</span>           <span class="hljs-comment">// 3.14</span>
<span class="hljs-string">"42"</span> - <span class="hljs-number">0</span>             <span class="hljs-comment">// 42</span>
<span class="hljs-string">"100"</span> - <span class="hljs-number">20</span>           <span class="hljs-comment">// 80</span>
</code></pre>
<h2 id="heading-number-to-string-conversions">Number to String Conversions</h2>
<h3 id="heading-string-concatenation">String Concatenation</h3>
<p>The simplest approach:</p>
<pre><code class="lang-javascript"><span class="hljs-number">42</span> + <span class="hljs-string">""</span>              <span class="hljs-comment">// "42"</span>
<span class="hljs-number">3.14</span> + <span class="hljs-string">""</span>            <span class="hljs-comment">// "3.14"</span>
(<span class="hljs-number">1</span> + <span class="hljs-number">2</span>) + <span class="hljs-string">""</span>         <span class="hljs-comment">// "3"</span>
</code></pre>
<h3 id="heading-template-literals">Template Literals</h3>
<p>Modern and readable:</p>
<pre><code class="lang-javascript"><span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-number">42</span>}</span>`</span>              <span class="hljs-comment">// "42"</span>
<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-number">3.14</span>}</span>`</span>            <span class="hljs-comment">// "3.14"</span>
<span class="hljs-string">`Value: <span class="hljs-subst">${<span class="hljs-number">100</span>}</span>`</span>      <span class="hljs-comment">// "Value: 100"</span>
</code></pre>
<h3 id="heading-string-constructor">String() Constructor</h3>
<p>Explicit conversion:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">String</span>(<span class="hljs-number">42</span>)           <span class="hljs-comment">// "42"</span>
<span class="hljs-built_in">String</span>(<span class="hljs-number">3.14</span>)         <span class="hljs-comment">// "3.14"</span>
<span class="hljs-built_in">String</span>(<span class="hljs-literal">null</span>)         <span class="hljs-comment">// "null"</span>
<span class="hljs-built_in">String</span>(<span class="hljs-literal">undefined</span>)    <span class="hljs-comment">// "undefined"</span>
</code></pre>
<h3 id="heading-tostring-method">toString() Method</h3>
<p>Offers base conversion options:</p>
<pre><code class="lang-javascript">(<span class="hljs-number">42</span>).toString()      <span class="hljs-comment">// "42"</span>
(<span class="hljs-number">255</span>).toString(<span class="hljs-number">16</span>)   <span class="hljs-comment">// "ff" (hexadecimal)</span>
(<span class="hljs-number">8</span>).toString(<span class="hljs-number">2</span>)      <span class="hljs-comment">// "1000" (binary)</span>
(<span class="hljs-number">255</span>).toString(<span class="hljs-number">8</span>)    <span class="hljs-comment">// "377" (octal)</span>
</code></pre>
<h3 id="heading-formatting-methods">Formatting Methods</h3>
<p>For precise decimal control:</p>
<pre><code class="lang-javascript">(<span class="hljs-number">3.14159</span>).toFixed(<span class="hljs-number">2</span>)     <span class="hljs-comment">// "3.14"</span>
(<span class="hljs-number">42</span>).toFixed(<span class="hljs-number">2</span>)          <span class="hljs-comment">// "42.00"</span>
(<span class="hljs-number">1234</span>).toExponential()   <span class="hljs-comment">// "1.234e+3"</span>
(<span class="hljs-number">123.456</span>).toPrecision(<span class="hljs-number">4</span>) <span class="hljs-comment">// "123.5"</span>
</code></pre>
<h2 id="heading-boolean-conversions">Boolean Conversions</h2>
<h3 id="heading-to-boolean-the-double-not">To Boolean: The Double NOT (!!)</h3>
<p>The standard way to convert any value to boolean:</p>
<pre><code class="lang-javascript">!!<span class="hljs-string">"hello"</span>            <span class="hljs-comment">// true</span>
!!<span class="hljs-number">1</span>                  <span class="hljs-comment">// true</span>
!!<span class="hljs-number">0</span>                  <span class="hljs-comment">// false</span>
!!<span class="hljs-string">""</span>                 <span class="hljs-comment">// false</span>
!!<span class="hljs-literal">null</span>               <span class="hljs-comment">// false</span>
!!<span class="hljs-literal">undefined</span>          <span class="hljs-comment">// false</span>
</code></pre>
<p><strong>Important:</strong> Arrays and objects are always truthy:</p>
<pre><code class="lang-javascript">!![]                 <span class="hljs-comment">// true (empty array is truthy!)</span>
!!{}                 <span class="hljs-comment">// true (empty object is truthy!)</span>
!!<span class="hljs-string">" "</span>                <span class="hljs-comment">// true (non-empty string)</span>
</code></pre>
<h3 id="heading-boolean-constructor">Boolean() Constructor</h3>
<p>More explicit alternative:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">Boolean</span>(<span class="hljs-string">"hello"</span>)     <span class="hljs-comment">// true</span>
<span class="hljs-built_in">Boolean</span>(<span class="hljs-number">0</span>)           <span class="hljs-comment">// false</span>
<span class="hljs-built_in">Boolean</span>([])          <span class="hljs-comment">// true</span>
</code></pre>
<h3 id="heading-from-boolean-to-other-types">From Boolean to Other Types</h3>
<p>Converting booleans to numbers:</p>
<pre><code class="lang-javascript"><span class="hljs-literal">true</span> + <span class="hljs-number">0</span>             <span class="hljs-comment">// 1</span>
<span class="hljs-literal">false</span> + <span class="hljs-number">0</span>            <span class="hljs-comment">// 0</span>
+<span class="hljs-literal">true</span>                <span class="hljs-comment">// 1</span>
+<span class="hljs-literal">false</span>               <span class="hljs-comment">// 0</span>
<span class="hljs-built_in">Number</span>(<span class="hljs-literal">true</span>)         <span class="hljs-comment">// 1</span>
<span class="hljs-built_in">Number</span>(<span class="hljs-literal">false</span>)        <span class="hljs-comment">// 0</span>
</code></pre>
<p>Converting booleans to strings:</p>
<pre><code class="lang-javascript"><span class="hljs-literal">true</span> + <span class="hljs-string">""</span>            <span class="hljs-comment">// "true"</span>
<span class="hljs-literal">false</span> + <span class="hljs-string">""</span>           <span class="hljs-comment">// "false"</span>
<span class="hljs-built_in">String</span>(<span class="hljs-literal">true</span>)         <span class="hljs-comment">// "true"</span>
<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-literal">false</span>}</span>`</span>           <span class="hljs-comment">// "false"</span>
</code></pre>
<h2 id="heading-array-and-string-conversions">Array and String Conversions</h2>
<h3 id="heading-array-to-string">Array to String</h3>
<p>Using join():</p>
<pre><code class="lang-javascript">[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].join()         <span class="hljs-comment">// "1,2,3"</span>
[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].join(<span class="hljs-string">""</span>)       <span class="hljs-comment">// "123"</span>
[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].join(<span class="hljs-string">"-"</span>)      <span class="hljs-comment">// "1-2-3"</span>
[<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>].join(<span class="hljs-string">" "</span>)<span class="hljs-comment">// "a b c"</span>
</code></pre>
<p>Quick conversion:</p>
<pre><code class="lang-javascript">[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].toString()     <span class="hljs-comment">// "1,2,3"</span>
[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>] + <span class="hljs-string">""</span>           <span class="hljs-comment">// "1,2,3"</span>
<span class="hljs-string">`<span class="hljs-subst">${[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]}</span>`</span>           <span class="hljs-comment">// "1,2,3"</span>
</code></pre>
<h3 id="heading-string-to-array">String to Array</h3>
<p>Multiple approaches:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Split method</span>
<span class="hljs-string">"hello"</span>.split(<span class="hljs-string">""</span>)            <span class="hljs-comment">// ['h', 'e', 'l', 'l', 'o']</span>
<span class="hljs-string">"a,b,c"</span>.split(<span class="hljs-string">","</span>)           <span class="hljs-comment">// ['a', 'b', 'c']</span>
<span class="hljs-string">"one two three"</span>.split(<span class="hljs-string">" "</span>)   <span class="hljs-comment">// ['one', 'two', 'three']</span>

<span class="hljs-comment">// Spread operator (ES6+)</span>
[...<span class="hljs-string">"hello"</span>]                 <span class="hljs-comment">// ['h', 'e', 'l', 'l', 'o']</span>
[...<span class="hljs-string">"abc"</span>]                   <span class="hljs-comment">// ['a', 'b', 'c']</span>

<span class="hljs-comment">// Array.from()</span>
<span class="hljs-built_in">Array</span>.from(<span class="hljs-string">"hello"</span>)          <span class="hljs-comment">// ['h', 'e', 'l', 'l', 'o']</span>
</code></pre>
<h2 id="heading-object-conversions">Object Conversions</h2>
<h3 id="heading-object-to-array">Object to Array</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> obj = {<span class="hljs-attr">a</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">b</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">c</span>: <span class="hljs-number">3</span>};

<span class="hljs-built_in">Object</span>.keys(obj)             <span class="hljs-comment">// ['a', 'b', 'c']</span>
<span class="hljs-built_in">Object</span>.values(obj)           <span class="hljs-comment">// [1, 2, 3]</span>
<span class="hljs-built_in">Object</span>.entries(obj)          <span class="hljs-comment">// [['a', 1], ['b', 2], ['c', 3]]</span>
</code></pre>
<h3 id="heading-array-to-object">Array to Object</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// From entries</span>
<span class="hljs-built_in">Object</span>.fromEntries([[<span class="hljs-string">'a'</span>, <span class="hljs-number">1</span>], [<span class="hljs-string">'b'</span>, <span class="hljs-number">2</span>]])
<span class="hljs-comment">// Result: {a: 1, b: 2}</span>

<span class="hljs-comment">// Using reduce</span>
[<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>].reduce(<span class="hljs-function">(<span class="hljs-params">acc, val, i</span>) =&gt;</span> ({...acc, [val]: i}), {})
<span class="hljs-comment">// Result: {a: 0, b: 1, c: 2}</span>

<span class="hljs-comment">// From keys with default value</span>
<span class="hljs-built_in">Object</span>.fromEntries([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>].map(<span class="hljs-function"><span class="hljs-params">k</span> =&gt;</span> [k, <span class="hljs-number">0</span>]))
<span class="hljs-comment">// Result: {a: 0, b: 0, c: 0}</span>
</code></pre>
<h3 id="heading-json-conversions">JSON Conversions</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> obj = {<span class="hljs-attr">name</span>: <span class="hljs-string">"Alice"</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span>};

<span class="hljs-comment">// Object to JSON string</span>
<span class="hljs-built_in">JSON</span>.stringify(obj)
<span class="hljs-comment">// '{"name":"Alice","age":25}'</span>

<span class="hljs-built_in">JSON</span>.stringify(obj, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>)
<span class="hljs-comment">// Formatted with 2-space indentation</span>

<span class="hljs-comment">// JSON string to Object</span>
<span class="hljs-built_in">JSON</span>.parse(<span class="hljs-string">'{"name":"Alice","age":25}'</span>)
<span class="hljs-comment">// {name: 'Alice', age: 25}</span>
</code></pre>
<h2 id="heading-special-cases-and-gotchas">Special Cases and Gotchas</h2>
<h3 id="heading-null-and-undefined-handling">Null and Undefined Handling</h3>
<p>Default values with nullish coalescing:</p>
<pre><code class="lang-javascript"><span class="hljs-literal">null</span> ?? <span class="hljs-string">"default"</span>        <span class="hljs-comment">// "default"</span>
<span class="hljs-literal">undefined</span> ?? <span class="hljs-string">"default"</span>   <span class="hljs-comment">// "default"</span>
<span class="hljs-number">0</span> ?? <span class="hljs-string">"default"</span>           <span class="hljs-comment">// 0 (not null/undefined)</span>
<span class="hljs-string">""</span> ?? <span class="hljs-string">"default"</span>          <span class="hljs-comment">// "" (not null/undefined)</span>
</code></pre>
<p>Default values with logical OR:</p>
<pre><code class="lang-javascript"><span class="hljs-literal">null</span> || <span class="hljs-string">"default"</span>        <span class="hljs-comment">// "default"</span>
<span class="hljs-number">0</span> || <span class="hljs-string">"default"</span>           <span class="hljs-comment">// "default" (0 is falsy)</span>
<span class="hljs-string">""</span> || <span class="hljs-string">"default"</span>          <span class="hljs-comment">// "default" (empty string is falsy)</span>
<span class="hljs-literal">false</span> || <span class="hljs-string">"default"</span>       <span class="hljs-comment">// "default"</span>
</code></pre>
<h3 id="heading-weird-coercions-you-should-know">Weird Coercions You Should Know</h3>
<p>These might surprise you:</p>
<pre><code class="lang-javascript">[] + []                  <span class="hljs-comment">// "" (empty string!)</span>
[] + {}                  <span class="hljs-comment">// "[object Object]"</span>
{} + []                  <span class="hljs-comment">// 0 (in some contexts)</span>
[] == <span class="hljs-literal">false</span>              <span class="hljs-comment">// true</span>
<span class="hljs-string">"0"</span> == <span class="hljs-number">0</span>                 <span class="hljs-comment">// true (loose equality coerces)</span>
<span class="hljs-string">"0"</span> === <span class="hljs-number">0</span>                <span class="hljs-comment">// false (strict equality doesn't)</span>
<span class="hljs-literal">null</span> == <span class="hljs-literal">undefined</span>        <span class="hljs-comment">// true</span>
<span class="hljs-literal">null</span> === <span class="hljs-literal">undefined</span>       <span class="hljs-comment">// false</span>
</code></pre>
<h3 id="heading-math-operations">Math Operations</h3>
<p>Addition vs other operators:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"5"</span> + <span class="hljs-number">3</span>                  <span class="hljs-comment">// "53" (concatenation)</span>
<span class="hljs-string">"5"</span> - <span class="hljs-number">3</span>                  <span class="hljs-comment">// 2 (conversion)</span>
<span class="hljs-string">"5"</span> * <span class="hljs-string">"2"</span>                <span class="hljs-comment">// 10 (both convert)</span>
<span class="hljs-string">"10"</span> / <span class="hljs-string">"2"</span>               <span class="hljs-comment">// 5 (both convert)</span>
</code></pre>
<h3 id="heading-date-conversions">Date Conversions</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-string">"2024-01-15"</span>);

<span class="hljs-comment">// Date to timestamp</span>
+date                    <span class="hljs-comment">// 1705276800000</span>
date.getTime()           <span class="hljs-comment">// 1705276800000</span>

<span class="hljs-comment">// Date to string</span>
date.toISOString()       <span class="hljs-comment">// "2024-01-15T00:00:00.000Z"</span>
date.toLocaleDateString()<span class="hljs-comment">// "1/15/2024"</span>

<span class="hljs-comment">// Timestamp to date</span>
<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">1705276800000</span>)  <span class="hljs-comment">// Mon Jan 15 2024</span>
</code></pre>
<h3 id="heading-number-base-conversions">Number Base Conversions</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Decimal to other bases</span>
(<span class="hljs-number">255</span>).toString(<span class="hljs-number">16</span>)       <span class="hljs-comment">// "ff" (hex)</span>
(<span class="hljs-number">255</span>).toString(<span class="hljs-number">2</span>)        <span class="hljs-comment">// "11111111" (binary)</span>
(<span class="hljs-number">255</span>).toString(<span class="hljs-number">8</span>)        <span class="hljs-comment">// "377" (octal)</span>

<span class="hljs-comment">// Other bases to decimal</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"ff"</span>, <span class="hljs-number">16</span>)       <span class="hljs-comment">// 255</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"11111111"</span>, <span class="hljs-number">2</span>)  <span class="hljs-comment">// 255</span>
<span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"377"</span>, <span class="hljs-number">8</span>)       <span class="hljs-comment">// 255</span>

<span class="hljs-comment">// Formatting</span>
(<span class="hljs-number">255</span>).toString(<span class="hljs-number">16</span>).toUpperCase()  <span class="hljs-comment">// "FF"</span>
(<span class="hljs-number">10</span>).toString(<span class="hljs-number">2</span>).padStart(<span class="hljs-number">8</span>, <span class="hljs-string">"0"</span>) <span class="hljs-comment">// "00001010"</span>
</code></pre>
<h2 id="heading-best-practices">Best Practices</h2>
<h3 id="heading-1-be-explicit-when-it-matters">1. Be Explicit When It Matters</h3>
<p>For production code, explicit conversions are often clearer:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Good</span>
<span class="hljs-built_in">Number</span>(userInput)
<span class="hljs-built_in">String</span>(value)
<span class="hljs-built_in">Boolean</span>(condition)

<span class="hljs-comment">// Clever but less clear</span>
+userInput
value + <span class="hljs-string">""</span>
!!condition
</code></pre>
<h3 id="heading-2-use-strict-equality">2. Use Strict Equality</h3>
<p>Always use <code>===</code> instead of <code>==</code> to avoid unexpected coercion:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Avoid</span>
<span class="hljs-keyword">if</span> (value == <span class="hljs-number">0</span>) { }      <span class="hljs-comment">// Could match "", [], false</span>

<span class="hljs-comment">// Prefer</span>
<span class="hljs-keyword">if</span> (value === <span class="hljs-number">0</span>) { }     <span class="hljs-comment">// Only matches 0</span>
</code></pre>
<h3 id="heading-3-handle-nan-properly">3. Handle NaN Properly</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Wrong way</span>
value === <span class="hljs-literal">NaN</span>            <span class="hljs-comment">// Always false (NaN !== NaN)</span>

<span class="hljs-comment">// Right way</span>
<span class="hljs-built_in">Number</span>.isNaN(value)      <span class="hljs-comment">// true only for NaN</span>
<span class="hljs-built_in">isNaN</span>(value)             <span class="hljs-comment">// true if value converts to NaN</span>
</code></pre>
<h3 id="heading-4-validate-before-converting">4. Validate Before Converting</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Safe conversion function</span>
<span class="hljs-keyword">const</span> toNumber = <span class="hljs-function">(<span class="hljs-params">str</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> num = +str;
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">isNaN</span>(num) ? <span class="hljs-number">0</span> : num;
};

toNumber(<span class="hljs-string">"42"</span>)           <span class="hljs-comment">// 42</span>
toNumber(<span class="hljs-string">"abc"</span>)          <span class="hljs-comment">// 0 (safe fallback)</span>
</code></pre>
<h3 id="heading-5-choose-the-right-method">5. Choose the Right Method</h3>
<p><strong>For integers from strings:</strong> Use <code>parseInt()</code> with radix</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">parseInt</span>(<span class="hljs-string">"42"</span>, <span class="hljs-number">10</span>)       <span class="hljs-comment">// Always specify base</span>
</code></pre>
<p><strong>For floats from strings:</strong> Use <code>parseFloat()</code> or <code>Number()</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">parseFloat</span>(<span class="hljs-string">"3.14"</span>)
<span class="hljs-built_in">Number</span>(<span class="hljs-string">"3.14"</span>)
</code></pre>
<p><strong>For formatting numbers:</strong> Use <code>toFixed()</code>, <code>toPrecision()</code></p>
<pre><code class="lang-javascript">(<span class="hljs-number">3.14159</span>).toFixed(<span class="hljs-number">2</span>)     <span class="hljs-comment">// "3.14"</span>
</code></pre>
<p><strong>For boolean checks:</strong> Use <code>Boolean()</code> or <code>!!</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (<span class="hljs-built_in">Boolean</span>(value)) { }
<span class="hljs-keyword">if</span> (!!value) { }
</code></pre>
<h2 id="heading-practical-examples">Practical Examples</h2>
<h3 id="heading-csv-string-to-number-array">CSV String to Number Array</h3>
<pre><code class="lang-javascript"><span class="hljs-string">"1,2,3,4,5"</span>.split(<span class="hljs-string">","</span>).map(<span class="hljs-built_in">Number</span>)
<span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
</code></pre>
<h3 id="heading-rgb-to-hex-color">RGB to Hex Color</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> rgbToHex = <span class="hljs-function">(<span class="hljs-params">r, g, b</span>) =&gt;</span> 
  <span class="hljs-string">"#"</span> + [r, g, b].map(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x.toString(<span class="hljs-number">16</span>).padStart(<span class="hljs-number">2</span>, <span class="hljs-string">"0"</span>)).join(<span class="hljs-string">""</span>);

rgbToHex(<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">128</span>)    <span class="hljs-comment">// "#ff0080"</span>
</code></pre>
<h3 id="heading-hex-to-rgb-color">Hex to RGB Color</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> hexToRgb = <span class="hljs-function"><span class="hljs-params">hex</span> =&gt;</span> 
  hex.match(<span class="hljs-regexp">/[A-Fa-f0-9]{2}/g</span>).map(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> <span class="hljs-built_in">parseInt</span>(x, <span class="hljs-number">16</span>));

hexToRgb(<span class="hljs-string">"#ff0080"</span>)      <span class="hljs-comment">// [255, 0, 128]</span>
</code></pre>
<h3 id="heading-query-parameters-to-object">Query Parameters to Object</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> params = <span class="hljs-string">"?name=Alice&amp;age=25"</span>;
<span class="hljs-built_in">Object</span>.fromEntries(<span class="hljs-keyword">new</span> URLSearchParams(params))
<span class="hljs-comment">// {name: 'Alice', age: '25'}</span>
</code></pre>
<h3 id="heading-safe-integer-conversion">Safe Integer Conversion</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> toInt = <span class="hljs-function">(<span class="hljs-params">value, defaultValue = <span class="hljs-number">0</span></span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> num = <span class="hljs-built_in">parseInt</span>(value, <span class="hljs-number">10</span>);
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">isNaN</span>(num) ? defaultValue : num;
};

toInt(<span class="hljs-string">"42"</span>)              <span class="hljs-comment">// 42</span>
toInt(<span class="hljs-string">"abc"</span>)             <span class="hljs-comment">// 0</span>
toInt(<span class="hljs-string">"abc"</span>, <span class="hljs-number">-1</span>)         <span class="hljs-comment">// -1</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>JavaScript's type conversion system is flexible and powerful, but it requires understanding to use effectively. Remember these key points:</p>
<ul>
<li><p>Use explicit conversions (<code>Number()</code>, <code>String()</code>, <code>Boolean()</code>) for clarity</p>
</li>
<li><p>Be aware of falsy values: <code>false</code>, <code>0</code>, <code>""</code>, <code>null</code>, <code>undefined</code>, <code>NaN</code></p>
</li>
<li><p>Empty arrays and objects are truthy</p>
</li>
<li><p>Use <code>===</code> instead of <code>==</code> to avoid unexpected coercion</p>
</li>
<li><p>Validate and handle edge cases like <code>NaN</code>, <code>null</code>, and <code>undefined</code></p>
</li>
<li><p>Choose the right method for the job: <code>parseInt()</code> for integers, <code>parseFloat()</code> for decimals</p>
</li>
<li><p>Test your conversions thoroughly, especially with user input</p>
</li>
</ul>
<p>Master these conversions, and you'll write more robust, predictable JavaScript code!</p>
]]></content:encoded></item><item><title><![CDATA[React Query: The Misunderstood Data-Fetching Library]]></title><description><![CDATA[Despite its name and reputation as a "data-fetching library," React Query doesn't actually fetch anything. It's a common misconception that needs clearing up.
What React Query Really Is
React Query is data-fetching agnostic. It's purely a caching and...]]></description><link>https://uidev.net/react-query-the-misunderstood-data-fetching-library</link><guid isPermaLink="true">https://uidev.net/react-query-the-misunderstood-data-fetching-library</guid><category><![CDATA[React]]></category><category><![CDATA[tanstack-query]]></category><category><![CDATA[fetch]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Mon, 26 Jan 2026 00:03:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769278760360/f85af230-bc78-489c-96b6-1eae25177a0e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Despite its name and reputation as a "data-fetching library," React Query doesn't actually fetch anything. It's a common misconception that needs clearing up.</p>
<h2 id="heading-what-react-query-really-is">What React Query Really Is</h2>
<p>React Query is <strong>data-fetching agnostic</strong>. It's purely a caching and state management library that doesn't care <em>how</em> you fetch your data. Whether you use <code>fetch()</code>, <code>axios</code>, <code>XMLHttpRequest</code>, or even a custom GraphQL client, React Query treats them all the same.</p>
<p>Think of React Query as a sophisticated data orchestrator. You're responsible for choosing a data fetcher, then React Query handles storing it, synchronizing it across components, and only refetching after the specified stale time.</p>
<h2 id="heading-understanding-the-architecture">Understanding the Architecture</h2>
<p>React Query's architecture separates concerns beautifully:</p>
<p><strong>Your Responsibility (The Data Fetcher):</strong></p>
<ul>
<li><p>Choose your HTTP client (fetch, axios, etc.)</p>
</li>
<li><p>Define API endpoints and request structure</p>
</li>
<li><p>Handle request formatting (REST, GraphQL, etc.)</p>
</li>
<li><p>Configure authentication and headers</p>
</li>
</ul>
<p><strong>React Query's Responsibility (The Cache Manager):</strong></p>
<ul>
<li><p>Store fetched data in memory</p>
</li>
<li><p>Track cache freshness and staleness</p>
</li>
<li><p>Coordinate when to refetch</p>
</li>
<li><p>Manage loading and error states across your app</p>
</li>
</ul>
<p>This separation means you have complete flexibility in how you retrieve data, while React Query handles the complex orchestration of keeping that data fresh and accessible.</p>
<h2 id="heading-you-can-use-any-fetching-method">You Can Use ANY Fetching Method</h2>
<p>Here's the beauty of React Query's agnostic approach—all of these work equally well:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Option 1: Native fetch()</span>
<span class="hljs-keyword">const</span> { data } = useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, userId],
  <span class="hljs-attr">queryFn</span>: <span class="hljs-function">() =&gt;</span> fetch(<span class="hljs-string">`/api/users/<span class="hljs-subst">${userId}</span>`</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
});

<span class="hljs-comment">// Option 2: Axios</span>
<span class="hljs-keyword">const</span> { data } = useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, userId],
  <span class="hljs-attr">queryFn</span>: <span class="hljs-function">() =&gt;</span> axios.get(<span class="hljs-string">`/api/users/<span class="hljs-subst">${userId}</span>`</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.data)
});

<span class="hljs-comment">// Option 3: GraphQL client</span>
<span class="hljs-keyword">const</span> { data } = useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, userId],
  <span class="hljs-attr">queryFn</span>: <span class="hljs-function">() =&gt;</span> graphqlClient.request(GET_USER_QUERY, { userId })
});

<span class="hljs-comment">// Option 4: Your custom API wrapper</span>
<span class="hljs-keyword">const</span> { data } = useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, userId],
  <span class="hljs-attr">queryFn</span>: <span class="hljs-function">() =&gt;</span> myCustomApi.users.getById(userId)
});
</code></pre>
<p>Notice the pattern? React Query only cares that your <code>queryFn</code> returns a Promise. Everything else is up to you.</p>
<h2 id="heading-what-react-query-does-handle">What React Query DOES Handle</h2>
<p>React Query excels at server state management and data synchronization:</p>
<p>✅ <strong>Caches results</strong> - Stores data in memory with configurable cache times, eliminating redundant requests</p>
<p>✅ <strong>Manages loading/error states</strong> - Provides <code>isLoading</code>, <code>isError</code>, <code>error</code>, <code>isFetching</code> without manual useState calls</p>
<p>✅ <strong>Handles refetching/invalidation</strong> - Automatically refetches stale data and invalidates caches when mutations occur</p>
<p>✅ <strong>Prevents duplicate requests</strong> - Deduplicates simultaneous requests for the same data across your entire app</p>
<p>✅ <strong>Background updates</strong> - Refetches data in the background when windows refocus, or network reconnects</p>
<p>✅ <strong>Pagination/infinite scroll helpers</strong> - Built-in utilities for complex data loading patterns</p>
<p>✅ <strong>Optimistic updates</strong> - Update UI immediately before the server confirms changes</p>
<p>✅ <strong>Global state synchronization</strong> - Automatically updates all components using the same query when data changes</p>
<p>✅ <strong>Request retry logic</strong> - Configurable retry attempts for failed requests</p>
<p>✅ <strong>Garbage collection</strong> - Removes unused cache entries to prevent memory leaks</p>
<h2 id="heading-what-react-query-doesnt-do">What React Query DOESN'T Do</h2>
<p>Understanding the boundaries is just as important:</p>
<p>❌ <strong>Make HTTP requests</strong> - You provide the fetching function</p>
<p>❌ <strong>Know about REST/GraphQL</strong> - It's protocol-agnostic</p>
<p>❌ <strong>Handle network layer</strong> - No built-in retry logic, interceptors, or request transformation (though you can add these yourself)</p>
<h2 id="heading-our-real-world-implementation">Our Real-World Implementation</h2>
<p>In our production codebase, we chose the native <code>fetch()</code> API to make GraphQL requests. React Query manages the entire data lifecycle:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> useProject = <span class="hljs-function">(<span class="hljs-params">projectId</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> useQuery({
    <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'project'</span>, projectId],
    <span class="hljs-attr">queryFn</span>: <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/graphql'</span>, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
        <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> },
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
          <span class="hljs-attr">query</span>: <span class="hljs-string">`
            query GetProject($id: ID!) {
              project(id: $id) {
                id
                name
                description
                updatedAt
              }
            }
          `</span>,
          <span class="hljs-attr">variables</span>: { <span class="hljs-attr">id</span>: projectId }
        })
      });

      <span class="hljs-keyword">const</span> { data, errors } = <span class="hljs-keyword">await</span> response.json();
      <span class="hljs-keyword">if</span> (errors) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errors[<span class="hljs-number">0</span>].message);
      <span class="hljs-keyword">return</span> data.project;
    },
    <span class="hljs-attr">staleTime</span>: <span class="hljs-number">5</span> * <span class="hljs-number">60</span> * <span class="hljs-number">000</span>, <span class="hljs-comment">// React Query manages staleness</span>
    <span class="hljs-attr">cacheTime</span>: <span class="hljs-number">10</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>, <span class="hljs-comment">// React Query manages cache</span>
  });
};
</code></pre>
<p>We handle the GraphQL request formatting and network call. React Query handles caching that response, tracking when it becomes stale, managing loading states across components, and automatically refetching when needed.</p>
<h2 id="heading-understanding-staletime-vs-cachetime-gctime">Understanding staleTime vs cacheTime (gcTime)</h2>
<p>Two of React Query's most important but confusing configurations are <code>staleTime</code> and <code>cacheTime</code> (renamed to <code>gcTime</code> in v5). They control different aspects of the data lifecycle:</p>
<p><strong>staleTime - When data becomes "stale"</strong></p>
<p>This determines how long data is considered "fresh." Fresh data won't trigger a refetch.</p>
<pre><code class="lang-javascript">useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, userId],
  <span class="hljs-attr">queryFn</span>: fetchUser,
  <span class="hljs-attr">staleTime</span>: <span class="hljs-number">5</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>, <span class="hljs-comment">// 5 minutes</span>
});
</code></pre>
<ul>
<li><p>Default: <code>0</code> (immediately stale)</p>
</li>
<li><p>When staleTime expires, data becomes "stale" but remains in cache</p>
</li>
<li><p>Stale data triggers background refetches on window focus, component mount, or manual refetch</p>
</li>
<li><p>Fresh data (within staleTime) won't refetch even if you revisit the component</p>
</li>
</ul>
<p><strong>cacheTime/gcTime - When cache gets garbage collected</strong></p>
<p>This determines how long unused data stays in memory after all components are unmounted.</p>
<pre><code class="lang-javascript">useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, userId],
  <span class="hljs-attr">queryFn</span>: fetchUser,
  <span class="hljs-attr">staleTime</span>: <span class="hljs-number">5</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,     <span class="hljs-comment">// Fresh for 5 minutes</span>
  <span class="hljs-attr">cacheTime</span>: <span class="hljs-number">10</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,    <span class="hljs-comment">// Cached for 10 minutes (v4)</span>
  <span class="hljs-comment">// gcTime: 10 * 60 * 1000,    // In v5, use gcTime instead</span>
});
</code></pre>
<ul>
<li><p>Default: <code>5 minutes</code></p>
</li>
<li><p>Starts counting down when the last component using this query unmounts</p>
</li>
<li><p>When gcTime expires, data is removed from memory completely</p>
</li>
<li><p>Next fetch will be a fresh request, not served from cache</p>
</li>
</ul>
<p><strong>How They Work Together:</strong></p>
<pre><code class="lang-javascript">Time:     <span class="hljs-number">0</span>s -------- <span class="hljs-number">5</span>min -------- <span class="hljs-number">10</span>min -------- <span class="hljs-number">15</span>min
          |           |             |              |
Mount --&gt; [--FRESH---][---STALE----]              |
          |           |             |              |
Unmount --------&gt;     |             |              |
                      |  [--CACHED--][--DELETED--]
</code></pre>
<p><strong>Real-world example:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// User profile - changes infrequently</span>
useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'profile'</span>],
  <span class="hljs-attr">queryFn</span>: fetchProfile,
  <span class="hljs-attr">staleTime</span>: <span class="hljs-number">10</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,  <span class="hljs-comment">// Don't refetch for 10 minutes</span>
  <span class="hljs-attr">gcTime</span>: <span class="hljs-number">30</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,     <span class="hljs-comment">// Keep in cache for 30 minutes</span>
});

<span class="hljs-comment">// Real-time stock prices - always fresh</span>
useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'stock'</span>, symbol],
  <span class="hljs-attr">queryFn</span>: fetchStock,
  <span class="hljs-attr">staleTime</span>: <span class="hljs-number">0</span>,               <span class="hljs-comment">// Always stale, always refetch</span>
  <span class="hljs-attr">gcTime</span>: <span class="hljs-number">1</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,      <span class="hljs-comment">// Clear from cache after 1 minute</span>
});

<span class="hljs-comment">// Static reference data - rarely changes</span>
useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'countries'</span>],
  <span class="hljs-attr">queryFn</span>: fetch
</code></pre>
<h2 id="heading-the-bottom-line">The Bottom Line</h2>
<p>React Query isn't a data-fetching library—it's a <strong>server state management and synchronization library</strong>. This distinction matters because it means you maintain full control over your data layer while delegating the complex state orchestration to a battle-tested solution.</p>
<h2 id="heading-why-state-management-matters">Why "State Management" Matters</h2>
<p>Traditional state management libraries like Redux or Zustand are designed for <strong>client state</strong>—data that lives entirely in your application (UI state, form inputs, user preferences). Server state is fundamentally different:</p>
<p><strong>Server State Characteristics:</strong></p>
<ul>
<li><p>Asynchronously fetched from remote sources</p>
</li>
<li><p>Can become outdated or "stale"</p>
</li>
<li><p>Shared across multiple components and users</p>
</li>
<li><p>Requires loading and error states</p>
</li>
<li><p>Needs periodic refreshing</p>
</li>
<li><p>Benefits of caching to reduce network requests</p>
</li>
</ul>
<p>React Query specializes in server state. It solves problems that client state managers weren't designed for:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Without React Query - manual state management</span>
<span class="hljs-keyword">const</span> [data, setData] = useState(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">const</span> [lastFetch, setLastFetch] = useState(<span class="hljs-literal">null</span>);

useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> fetchData = <span class="hljs-keyword">async</span> () =&gt; {
    setIsLoading(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/data'</span>);
      setData(<span class="hljs-keyword">await</span> result.json());
      setLastFetch(<span class="hljs-built_in">Date</span>.now());
    } <span class="hljs-keyword">catch</span> (err) {
      setError(err);
    } <span class="hljs-keyword">finally</span> {
      setIsLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-comment">// Need to track staleness manually</span>
  <span class="hljs-keyword">const</span> isStale = !lastFetch || <span class="hljs-built_in">Date</span>.now() - lastFetch &gt; <span class="hljs-number">60000</span>;
  <span class="hljs-keyword">if</span> (isStale) fetchData();
}, [lastFetch]);

<span class="hljs-comment">// With React Query - automatic server state management</span>
<span class="hljs-keyword">const</span> { data, isLoading, error } = useQuery({
  <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'data'</span>],
  <span class="hljs-attr">queryFn</span>: <span class="hljs-function">() =&gt;</span> fetch(<span class="hljs-string">'/api/data'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()),
  <span class="hljs-attr">staleTime</span>: <span class="hljs-number">60000</span>
});
</code></pre>
<p>React Query automatically handles what you'd need dozens of lines to manage manually: caching, staleness tracking, background refetching, request deduplication, and state synchronization across components.</p>
<h2 id="heading-using-react-query-as-global-state-management">Using React Query as Global State Management</h2>
<p>One of React Query's most powerful features is automatic global state synchronization. When you fetch data with the same <code>queryKey</code> in multiple components, they all share the same cached data and update together:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// UserProfile.jsx</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserProfile</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { <span class="hljs-attr">data</span>: user } = useQuery({
    <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, <span class="hljs-string">'current'</span>],
    <span class="hljs-attr">queryFn</span>: fetchCurrentUser,
  });

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-comment">// UserAvatar.jsx (different component, same data!)</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserAvatar</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { <span class="hljs-attr">data</span>: user } = useQuery({
    <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'user'</span>, <span class="hljs-string">'current'</span>],
    <span class="hljs-attr">queryFn</span>: fetchCurrentUser,
  });

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{user.avatar}</span> /&gt;</span></span>;
}

<span class="hljs-comment">// Settings.jsx (updates everywhere automatically)</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Settings</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> queryClient = useQueryClient();

  <span class="hljs-keyword">const</span> mutation = useMutation({
    <span class="hljs-attr">mutationFn</span>: updateUserName,
    <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">updatedUser</span>) =&gt;</span> {
      <span class="hljs-comment">// This updates BOTH UserProfile and UserAvatar instantly</span>
      queryClient.setQueryData([<span class="hljs-string">'user'</span>, <span class="hljs-string">'current'</span>], updatedUser);
    }
  });

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> mutation.mutate({ name: 'New Name' })}&gt;
    Update Name
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>;
}
</code></pre>
<p><strong>What just happened?</strong></p>
<ol>
<li><p>Multiple components use the same <code>queryKey: ['user', 'current']</code></p>
</li>
<li><p>React Query fetches <strong>once</strong> and shares the result</p>
</li>
<li><p>When we update the cache with <code>setQueryData</code>, <strong>all components re-render</strong> with new data</p>
</li>
<li><p>No props drilling, no context providers, no manual subscriptions</p>
</li>
</ol>
<p>This replaces traditional global state patterns:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Traditional approach - lots of boilerplate</span>
<span class="hljs-keyword">const</span> UserContext = createContext();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserProvider</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">const</span> [user, setUser] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchUser().then(setUser);
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">user</span>, <span class="hljs-attr">setUser</span>, <span class="hljs-attr">loading</span> }}&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">UserContext.Provider</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// React Query approach - automatic</span>
<span class="hljs-comment">// Just use the same queryKey wherever you need the data!</span>
</code></pre>
<p>The <code>queryKey</code> acts as a global identifier. Any component using that key gets the same data, loading states, and updates—automatically.</p>
]]></content:encoded></item><item><title><![CDATA[Debugging React Apps with MobX-State-Tree]]></title><description><![CDATA[From Redux to MST
If you're like me, coming from the Redux world, you'll soon find yourself missing the Redux DevTools. Being able to see your entire store at any moment, with time-travel debugging and a clear action log, is incredibly powerful. But ...]]></description><link>https://uidev.net/debugging-react-apps-with-mobx-state-tree</link><guid isPermaLink="true">https://uidev.net/debugging-react-apps-with-mobx-state-tree</guid><category><![CDATA[React]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[MobX]]></category><category><![CDATA[mobx-state-tree]]></category><category><![CDATA[Redux]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Sun, 04 Jan 2026 23:59:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767571293780/58bfbcc8-a239-4ac0-9133-305ae18203d1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-from-redux-to-mst">From Redux to MST</h2>
<p>If you're like me, coming from the Redux world, you'll soon find yourself missing the Redux DevTools. Being able to see your entire store at any moment, with time-travel debugging and a clear action log, is incredibly powerful. But as I learned more about MST, I started to appreciate its different approach.</p>
<p>MST feels lighter than Redux—no boilerplate for actions and reducers, and stores are scoped to specific data types rather than one giant global store. Compared to plain MobX, MST's structured model definitions are more elegant, with built-in type checking and snapshot-based time travel.</p>
<p>The trade-off? Debugging requires a different mindset. I tried several browser extensions, but they're either deprecated or don't work well with MST. There's no magic DevTools button. Instead, you'll need to instrument your code with MST's built-in debugging tools.</p>
<p>The good news? Once you learn these patterns, MST debugging becomes just as powerful—and in some ways, more flexible—than Redux DevTools.</p>
<h2 id="heading-1-the-three-built-in-debugging-tools">1. The Three Built-in Debugging Tools</h2>
<p>MST provides three powerful listeners that form the foundation of debugging:</p>
<h3 id="heading-onaction-track-what-happened">onAction - Track What Happened</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { onAction } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-state-tree'</span>;

onAction(rootStore, <span class="hljs-function"><span class="hljs-params">call</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Action called:'</span>, {
    <span class="hljs-attr">name</span>: call.name,      <span class="hljs-comment">// toggleTodo</span>
    <span class="hljs-attr">path</span>: call.path,      <span class="hljs-comment">// /todos/0</span>
    <span class="hljs-attr">args</span>: call.args       <span class="hljs-comment">// []</span>
  });
});
</code></pre>
<p>This tells you <strong>what action</strong> was called, <strong>where</strong> it was called, and <strong>what arguments</strong> were passed.</p>
<h3 id="heading-onpatch-track-what-changed">onPatch - Track What Changed</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { onPatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-state-tree'</span>;

onPatch(rootStore, <span class="hljs-function"><span class="hljs-params">patch</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'State changed:'</span>, {
    <span class="hljs-attr">op</span>: patch.op,         <span class="hljs-comment">// "replace", "add", or "remove"</span>
    <span class="hljs-attr">path</span>: patch.path,     <span class="hljs-comment">// "/todos/0/completed"</span>
    <span class="hljs-attr">value</span>: patch.value    <span class="hljs-comment">// true</span>
  });
});
</code></pre>
<p>This shows you the <strong>exact property</strong> that changed and its <strong>new value</strong>. This is usually the most useful for debugging.</p>
<h3 id="heading-onsnapshot-track-full-state">onSnapshot - Track Full State</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { onSnapshot } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-state-tree'</span>;

onSnapshot(rootStore, <span class="hljs-function"><span class="hljs-params">snapshot</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Current state:'</span>, snapshot);
});
</code></pre>
<p>This gives you the <strong>entire state tree</strong> after each change. Use sparingly as it can be verbose.</p>
<h2 id="heading-2-setting-up-a-debugging-environment">2. Setting Up a Debugging Environment</h2>
<p>Here's a practical setup for development:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// store/debugger.js</span>
<span class="hljs-keyword">import</span> { onAction, onPatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-state-tree'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setupDebugger</span>(<span class="hljs-params">store</span>) </span>{
  <span class="hljs-keyword">if</span> (process.env.NODE_ENV !== <span class="hljs-string">'development'</span>) <span class="hljs-keyword">return</span>;

  <span class="hljs-comment">// Track actions with grouping for readability</span>
  onAction(store, <span class="hljs-function"><span class="hljs-params">call</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.group(<span class="hljs-string">`🎬 <span class="hljs-subst">${call.name}</span>`</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Path:'</span>, call.path);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Args:'</span>, call.args);
    <span class="hljs-built_in">console</span>.groupEnd();
  });

  <span class="hljs-comment">// Track patches with colored output</span>
  onPatch(store, <span class="hljs-function"><span class="hljs-params">patch</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> emoji = {
      <span class="hljs-attr">replace</span>: <span class="hljs-string">'✏️'</span>,
      <span class="hljs-attr">add</span>: <span class="hljs-string">'➕'</span>,
      <span class="hljs-attr">remove</span>: <span class="hljs-string">'➖'</span>
    }[patch.op];

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${emoji}</span> <span class="hljs-subst">${patch.path}</span>`</span>, patch.value);
  });

  <span class="hljs-comment">// Make store accessible in console</span>
  <span class="hljs-built_in">window</span>.store = store;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'💡 Store available as window.store'</span>);
}

<span class="hljs-comment">// store/index.js</span>
<span class="hljs-keyword">import</span> { setupDebugger } <span class="hljs-keyword">from</span> <span class="hljs-string">'./debugger'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createRootStore</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> store = RootStore.create({ <span class="hljs-comment">/* ... */</span> });
  setupDebugger(store);
  <span class="hljs-keyword">return</span> store;
}
</code></pre>
<p>Now every action and state change is logged automatically!</p>
<h2 id="heading-3-debugging-component-re-renders">3. Debugging Component Re-renders</h2>
<p>One common issue: "Why is my component re-rendering?"</p>
<h3 id="heading-use-react-devtools-profiler">Use React DevTools Profiler</h3>
<p>First, use React DevTools to see <strong>what</strong> is re-rendering:</p>
<ol>
<li><p>Install React DevTools extension</p>
</li>
<li><p>Open DevTools → Profiler tab</p>
</li>
<li><p>Click record → interact with app → stop</p>
</li>
<li><p>See which components rendered and why</p>
</li>
</ol>
<h3 id="heading-add-trace-to-see-observables">Add trace() to See Observables</h3>
<p>Use MobX's <code>trace()</code> to see what observables a component is tracking:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { observer } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-react-lite'</span>;
<span class="hljs-keyword">import</span> { trace } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx'</span>;

<span class="hljs-keyword">const</span> TodoList = observer(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todoStore } = useStores();

  <span class="hljs-comment">// Add this temporarily</span>
  trace();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {todoStore.todos.map(todo =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">TodoItem</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span> <span class="hljs-attr">todo</span>=<span class="hljs-string">{todo}</span> /&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
  );
});
</code></pre>
<p>When this component re-renders, you'll see in the console which observable triggered it.</p>
<h2 id="heading-common-re-render-issues">Common Re-render Issues</h2>
<p><strong>Problem 1: Missing observer wrapper</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ❌ Won't update when todo changes</span>
<span class="hljs-keyword">const</span> TodoItem = <span class="hljs-function">(<span class="hljs-params">{ todo }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
};

<span class="hljs-comment">// ✅ Will update</span>
<span class="hljs-keyword">const</span> TodoItem = observer(<span class="hljs-function">(<span class="hljs-params">{ todo }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
});
</code></pre>
<p><strong>Problem 2: Over-observing</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ❌ Re-renders when ANY todo changes</span>
<span class="hljs-keyword">const</span> TodoList = observer(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todoStore } = useStores();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      Completed: {todoStore.todos.filter(t =&gt; t.completed).length}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
});

<span class="hljs-comment">// ✅ Only re-renders when completed count changes</span>
<span class="hljs-keyword">const</span> TodoStore = types.model({
  <span class="hljs-attr">todos</span>: types.array(Todo),
}).views(<span class="hljs-function"><span class="hljs-params">self</span> =&gt;</span> ({
  <span class="hljs-keyword">get</span> <span class="hljs-title">completedCount</span>() {
    <span class="hljs-keyword">return</span> self.todos.filter(<span class="hljs-function"><span class="hljs-params">t</span> =&gt;</span> t.completed).length;
  }
}));

<span class="hljs-keyword">const</span> TodoList = observer(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todoStore } = useStores();
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Completed: {todoStore.completedCount}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
});
</code></pre>
<p>Move calculations to computed values (views) in your store!</p>
<h2 id="heading-4-time-travel-debugging">4. Time Travel Debugging</h2>
<p>One of MST's superpowers is snapshots. Here's how to implement undo/redo:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { applySnapshot, getSnapshot } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-state-tree'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeTravel</span> </span>{
  <span class="hljs-keyword">constructor</span>(store) {
    <span class="hljs-built_in">this</span>.store = store;
    <span class="hljs-built_in">this</span>.history = [getSnapshot(store)];
    <span class="hljs-built_in">this</span>.currentIndex = <span class="hljs-number">0</span>;

    onSnapshot(store, <span class="hljs-function"><span class="hljs-params">snapshot</span> =&gt;</span> {
      <span class="hljs-comment">// Remove any "future" states if we went back</span>
      <span class="hljs-built_in">this</span>.history.splice(<span class="hljs-built_in">this</span>.currentIndex + <span class="hljs-number">1</span>);
      <span class="hljs-built_in">this</span>.history.push(snapshot);
      <span class="hljs-built_in">this</span>.currentIndex = <span class="hljs-built_in">this</span>.history.length - <span class="hljs-number">1</span>;
    });
  }

  undo() {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.currentIndex &gt; <span class="hljs-number">0</span>) {
      <span class="hljs-built_in">this</span>.currentIndex--;
      applySnapshot(<span class="hljs-built_in">this</span>.store, <span class="hljs-built_in">this</span>.history[<span class="hljs-built_in">this</span>.currentIndex]);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'⏪ Undo'</span>);
    }
  }

  redo() {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.currentIndex &lt; <span class="hljs-built_in">this</span>.history.length - <span class="hljs-number">1</span>) {
      <span class="hljs-built_in">this</span>.currentIndex++;
      applySnapshot(<span class="hljs-built_in">this</span>.store, <span class="hljs-built_in">this</span>.history[<span class="hljs-built_in">this</span>.currentIndex]);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'⏩ Redo'</span>);
    }
  }
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">const</span> timeTravel = <span class="hljs-keyword">new</span> TimeTravel(rootStore);
<span class="hljs-built_in">window</span>.undo = <span class="hljs-function">() =&gt;</span> timeTravel.undo();
<span class="hljs-built_in">window</span>.redo = <span class="hljs-function">() =&gt;</span> timeTravel.redo();
</code></pre>
<p>Now you can type <code>undo()</code> or <code>redo()</code> in the console to step through state changes!</p>
<h2 id="heading-5-debugging-async-actions">5. Debugging Async Actions</h2>
<p>Async actions can be particularly tricky. Here's how to add proper logging:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> TodoStore = types.model({
  <span class="hljs-attr">todos</span>: types.array(Todo),
  <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">error</span>: <span class="hljs-literal">null</span>,
}).actions(<span class="hljs-function"><span class="hljs-params">self</span> =&gt;</span> ({
  <span class="hljs-keyword">async</span> fetchTodos() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'📡 Fetching todos...'</span>);
    self.isLoading = <span class="hljs-literal">true</span>;
    self.error = <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/todos'</span>);
      <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'✅ Fetched:'</span>, data.length, <span class="hljs-string">'todos'</span>);
      self.todos = data;
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'❌ Fetch failed:'</span>, err);
      self.error = err.message;
    } <span class="hljs-keyword">finally</span> {
      self.isLoading = <span class="hljs-literal">false</span>;
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'📡 Fetch complete'</span>);
    }
  }
}));
</code></pre>
<p>Use try-catch blocks and log at each stage to see where things break.</p>
<h2 id="heading-6-production-debugging">6. Production Debugging</h2>
<p>You can't use console.log in production, but you can store logs:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MSTLogger</span> </span>{
  <span class="hljs-keyword">constructor</span>(store, maxLogs = 100) {
    <span class="hljs-built_in">this</span>.logs = [];

    onAction(store, <span class="hljs-function"><span class="hljs-params">call</span> =&gt;</span> {
      <span class="hljs-built_in">this</span>.addLog({
        <span class="hljs-attr">type</span>: <span class="hljs-string">'action'</span>,
        <span class="hljs-attr">name</span>: call.name,
        <span class="hljs-attr">path</span>: call.path,
        <span class="hljs-attr">args</span>: call.args,
        <span class="hljs-attr">timestamp</span>: <span class="hljs-built_in">Date</span>.now()
      });
    });

    onPatch(store, <span class="hljs-function"><span class="hljs-params">patch</span> =&gt;</span> {
      <span class="hljs-built_in">this</span>.addLog({
        <span class="hljs-attr">type</span>: <span class="hljs-string">'patch'</span>,
        <span class="hljs-attr">op</span>: patch.op,
        <span class="hljs-attr">path</span>: patch.path,
        <span class="hljs-attr">value</span>: patch.value,
        <span class="hljs-attr">timestamp</span>: <span class="hljs-built_in">Date</span>.now()
      });
    });
  }

  addLog(log) {
    <span class="hljs-built_in">this</span>.logs.push(log);
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.logs.length &gt; <span class="hljs-number">100</span>) {
      <span class="hljs-built_in">this</span>.logs.shift(); <span class="hljs-comment">// Keep last 100</span>
    }
  }

  <span class="hljs-keyword">export</span>() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.logs, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>);
  }

  sendToErrorTracking() {
    <span class="hljs-comment">// Send to Sentry, LogRocket, etc.</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.Sentry) {
      <span class="hljs-built_in">window</span>.Sentry.captureMessage(<span class="hljs-string">'MST Logs'</span>, {
        <span class="hljs-attr">extra</span>: { <span class="hljs-attr">logs</span>: <span class="hljs-built_in">this</span>.logs }
      });
    }
  }
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">const</span> logger = <span class="hljs-keyword">new</span> MSTLogger(rootStore);
<span class="hljs-built_in">window</span>.exportLogs = <span class="hljs-function">() =&gt;</span> logger.export();
</code></pre>
<p>When a user reports a bug, ask them to run <code>exportLogs()</code> in the console and send you the output!</p>
<h2 id="heading-7-mst-devtools">7. MST DevTools</h2>
<p>For the best debugging experience, use the MST Inspector:</p>
<pre><code class="lang-bash">npm install --save-dev mobx-state-tree-inspector
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { makeInspectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'mobx-state-tree-inspector'</span>;

<span class="hljs-keyword">if</span> (process.env.NODE_ENV === <span class="hljs-string">'development'</span>) {
  makeInspectable(rootStore);
}
</code></pre>
<p>This adds a visual tree inspector to Chrome DevTools showing your entire state tree and all changes in real-time.</p>
<h2 id="heading-8-common-debugging-scenarios">8. Common Debugging Scenarios</h2>
<h3 id="heading-my-component-isnt-updating">"My component isn't updating"</h3>
<p><strong>Checklist:</strong></p>
<ol>
<li><p>Is the component wrapped with <code>observer</code>?</p>
</li>
<li><p>Is it reading from an MST observable?</p>
</li>
<li><p>Is the data actually changing? (Add a patch listener)</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-comment">// Add this temporarily</span>
onPatch(rootStore.todos, <span class="hljs-function"><span class="hljs-params">patch</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Todo changed:'</span>, patch);
});
</code></pre>
<h3 id="heading-too-many-re-renders">"Too many re-renders"</h3>
<p><strong>Checklist:</strong></p>
<ol>
<li><p>Are you creating new objects/functions in render?</p>
</li>
<li><p>Are you observing too much?</p>
</li>
<li><p>Move calculations to computed values</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-comment">// ❌ Bad - creates new function every render</span>
&lt;button onClick={<span class="hljs-function">() =&gt;</span> store.addTodo(text)}&gt;Add&lt;/button&gt;

<span class="hljs-comment">// ✅ Good - stable reference</span>
<span class="hljs-keyword">const</span> handleAdd = <span class="hljs-function">() =&gt;</span> store.addTodo(text);
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleAdd}</span>&gt;</span>Add<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre>
<h3 id="heading-state-is-wrong-after-api-call">"State is wrong after API call"</h3>
<p><strong>Debug:</strong></p>
<ol>
<li><p>Log the API response</p>
</li>
<li><p>Check the action that processes it</p>
</li>
<li><p>Verify the model types match the data</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> fetchTodos() {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/todos'</span>);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'API returned:'</span>, data);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Current todos:'</span>, getSnapshot(self.todos));

  self.todos = data;

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'New todos:'</span>, getSnapshot(self.todos));
}
</code></pre>
<p>Note: It’s recommended to use <a target="_blank" href="https://mobx-state-tree.js.org/concepts/async-actions">MST’s flow</a> for async calls to keep the transaction atomic.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { types, flow } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx-state-tree"</span>;

<span class="hljs-keyword">const</span> UserStore = types
  .model(<span class="hljs-string">"UserStore"</span>, {
    <span class="hljs-attr">users</span>: types.array(types.frozen()),
    <span class="hljs-comment">// Track the lifecycle of the request</span>
    <span class="hljs-attr">status</span>: types.optional(
      types.enumeration([<span class="hljs-string">"idle"</span>, <span class="hljs-string">"pending"</span>, <span class="hljs-string">"done"</span>, <span class="hljs-string">"error"</span>]), 
      <span class="hljs-string">"idle"</span>
    )
  })
  .actions(<span class="hljs-function">(<span class="hljs-params">self</span>) =&gt;</span> ({
    <span class="hljs-comment">// Use a generator function (note the *)</span>
    <span class="hljs-attr">fetchUsers</span>: flow(<span class="hljs-function"><span class="hljs-keyword">function</span>* (<span class="hljs-params"></span>) </span>{
      self.status = <span class="hljs-string">"pending"</span>;
      <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// Use yield instead of await</span>
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">yield</span> fetch(<span class="hljs-string">"https://api.example.com/users"</span>);
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">yield</span> response.json();

        <span class="hljs-comment">// MST automatically wraps this update in an action context</span>
        self.users = data;
        self.status = <span class="hljs-string">"done"</span>;
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to fetch users:"</span>, error);
        self.status = <span class="hljs-string">"error"</span>;
      }
    })
  }));
</code></pre>
<h2 id="heading-9-best-practices">9. Best Practices</h2>
<ol>
<li><p><strong>Use TypeScript</strong> - Catch type errors before runtime</p>
</li>
<li><p><strong>Keep stores small</strong> - Easier to reason about and debug</p>
</li>
<li><p><strong>Use computed values in view</strong> - They're cached and easier to track</p>
</li>
<li><p><strong>Name your actions</strong> - Clear names make logs more useful</p>
</li>
<li><p><strong>One action per user interaction</strong> - Makes debugging straightforward</p>
</li>
</ol>
<h2 id="heading-10-quick-debug-snippet">10. Quick Debug Snippet</h2>
<p>Add this to your store for instant debugging:</p>
<pre><code class="lang-javascript">.views(<span class="hljs-function"><span class="hljs-params">self</span> =&gt;</span> ({
  <span class="hljs-keyword">get</span> <span class="hljs-title">debug</span>() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">snapshot</span>: getSnapshot(self),
      <span class="hljs-comment">// Add any useful computed data</span>
      <span class="hljs-attr">todoCount</span>: self.todos.length,
      <span class="hljs-attr">completedCount</span>: self.todos.filter(<span class="hljs-function"><span class="hljs-params">t</span> =&gt;</span> t.completed).length,
    };
  }
}));

<span class="hljs-comment">// In console</span>
<span class="hljs-built_in">console</span>.table(rootStore.debug);
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Debugging MST apps is different from Redux, but once you know the tools, it's actually quite powerful. The key insights:</p>
<ul>
<li><p>Use <strong>onPatch</strong> to see what changed</p>
</li>
<li><p>Use <strong>onAction</strong> to see what caused it</p>
</li>
<li><p>Use <strong>trace()</strong> to debug component re-renders</p>
</li>
<li><p>Use <strong>snapshots</strong> for time travel</p>
</li>
<li><p>Set up logging in development mode</p>
</li>
<li><p>Use <strong>flow()</strong> for async calls</p>
</li>
</ul>
<p>With these tools in your belt, you'll spend less time debugging and more time building features. Happy debugging!</p>
<hr />
<p><strong>Resources:</strong></p>
<ul>
<li><p><a target="_blank" href="https://mobx-state-tree.js.org/">MST Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/mobxjs/mobx-devtools">MobX DevTools</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/coolcatcoder/mobx-state-tree-inspector">MST Inspector</a></p>
</li>
<li><p><a target="_blank" href="https://mobx-state-tree.js.org/concepts/async-actions">MST Asynchronous Actions</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[SolidJS Reactivity System]]></title><description><![CDATA[SolidJS is a modern JavaScript framework for building user interfaces. You may have just heard about it recently, but it’s not new. Here are a few things to know about SolidJS and how it compares with React.
What It Is
SolidJS is a declarative JavaSc...]]></description><link>https://uidev.net/solidjs-reactivity-system</link><guid isPermaLink="true">https://uidev.net/solidjs-reactivity-system</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[solidjs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Sun, 30 Nov 2025 01:02:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763074570852/6c86df53-968c-4953-91a5-373877e8afb6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>SolidJS is a modern JavaScript framework for building user interfaces. You may have just heard about it recently, but it’s not new. Here are a few things to know about SolidJS and how it compares with React.</p>
<h2 id="heading-what-it-is"><strong>What It Is</strong></h2>
<p>SolidJS is a declarative JavaScript library for creating web applications with a focus on performance and fine-grained reactivity. Its development began in 2016 by Ryan Carniato, with the first commit to the SolidJS repository occurring on August 21, 2016. The framework's official 1.0 release was in June 2021. It has gained significant popularity in recent years.</p>
<h2 id="heading-key-features"><strong>Key Features</strong></h2>
<ul>
<li><p><strong>Fine-grained reactivity</strong>: SolidJS uses a reactive system that updates only the specific parts of the DOM that need to change, making it extremely fast.</p>
</li>
<li><p><strong>No Virtual DOM</strong>: SolidJS compiles your code to real DOM operations, which contributes to its excellent performance.</p>
</li>
<li><p><strong>Familiar syntax</strong>: If you know React, SolidJS will look very familiar. It uses JSX and has a component-based architecture with similar patterns to <code>useState</code> (called <code>createSignal</code> in Solid).</p>
</li>
<li><p><strong>Small bundle size</strong>: The framework itself is lightweight, typically resulting in smaller production bundles.</p>
</li>
<li><p><strong>True reactivity</strong>: State updates automatically propagate through your application without needing to worry about dependency arrays or optimization techniques.</p>
</li>
</ul>
<h2 id="heading-how-does-it-work">How Does It Work</h2>
<p>SolidJS's reactivity has three main components working together:</p>
<ol>
<li><p><strong>Signals</strong> - Hold reactive values and track their subscribers</p>
</li>
<li><p><strong>Effects</strong> - Functions that auto-run when their dependencies change</p>
</li>
<li><p><strong>Direct DOM references</strong> - Stored in closures, accessed by effects</p>
</li>
</ol>
<p><strong>Phase 1: Set up signals and bind to DOM notes</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763064175379/246c2de3-37af-4b5a-9e29-bf7d224bceca.png" alt class="image--center mx-auto" /></p>
<p><strong>Phase 2: Trigger updates in signal and notify subscribers (DOM nodes)</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763064248249/12066fd0-8e9e-45cf-bf4c-5e259b59deec.png" alt class="image--center mx-auto" /></p>
<p>The brilliance of SolidJS:</p>
<ul>
<li><p><strong>Signals</strong> track which effects depend on them (the dependency graph)</p>
</li>
<li><p><strong>Effects</strong> hold direct references to DOM nodes (the O(1) access)</p>
</li>
<li><p>When a signal changes, it notifies its effects, which update their DOM nodes directly</p>
</li>
</ul>
<h2 id="heading-solidjs-vs-reactjs">SolidJS vs. ReactJS</h2>
<p>React uses the <strong>Virtual DOM</strong> to make DOM updates easier and more predictable for developers, not necessarily for raw performance. The Virtual DOM is actually an <em>abstraction layer</em> that adds overhead - it requires:</p>
<ol>
<li><p>Creating a virtual representation of the UI in memory</p>
</li>
<li><p>Comparing (diffing) the old and new virtual trees</p>
</li>
<li><p>Then updating the real DOM</p>
</li>
</ol>
<p>SolidJS can be more performant with its <strong>fine-grained reactivity</strong> and <strong>compile-time optimizations</strong>:</p>
<ol>
<li><p><strong>Direct DOM updates</strong>: When you write SolidJS code, the compiler analyzes your components at build time and generates code that updates specific DOM nodes directly when state changes—no diffing needed.</p>
</li>
<li><p><strong>Surgical precision</strong>: Instead of re-running component functions and comparing trees, SolidJS creates reactive "subscriptions" that know exactly which DOM nodes depend on which pieces of state. When state changes, only those exact nodes update.</p>
</li>
<li><p><strong>One-time setup</strong>: The component function runs only once during creation. After that, only the reactive values update, triggering their specific DOM updates.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[React Server and Client Components Explained]]></title><description><![CDATA[React Server Components (RSCs) are a new component type introduced in React 18 and stabilized in React 19. Next.js picked it up right after it came out, and it has been production-ready since v13.
RSCs run exclusively on the server, generate HTML out...]]></description><link>https://uidev.net/react-server-and-client-components-explained</link><guid isPermaLink="true">https://uidev.net/react-server-and-client-components-explained</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[rsc]]></category><category><![CDATA[web performance]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Thu, 13 Nov 2025 21:06:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xkBaqlcqeb4/upload/63079547c35ed2f8d317ca94fce73c5f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://react.dev/reference/rsc/server-components">React Server Components (RSCs)</a> are a new component type introduced in React 18 and stabilized in React 19. Next.js picked it up right after it came out, and it has been production-ready since v13.</p>
<p>RSCs run exclusively on the server, generate HTML output, and the component code is not sent to the browser. Unlike the traditional Client Components that render in the browser, RSCs execute on the server. RSCs can directly access server-side resources, such as databases or file systems, without requiring a separate API layer. On the flip side, they can’t access browser APIs or hooks, which should be done in the Client Components.</p>
<h2 id="heading-server-components-vs-client-components">Server Components vs. Client Components</h2>
<p>Starting from React 19, to define a Client Component requires adding <code>'use client'</code> at the top of the file.</p>
<p>If the component needs any of the items listed below, it must be a Client Component.</p>
<ul>
<li><p>React hooks (useState, useEffect, etc.)</p>
</li>
<li><p>Browser APIs (window, document)</p>
</li>
<li><p>Event handlers (onClick, onChange)</p>
</li>
<li><p>Third-party libraries that use browser-only features</p>
</li>
</ul>
<p>The default for a component, without either of these directives, is a Server Component.</p>
<p>Read more on <a target="_blank" href="https://nextjs.org/docs/app/getting-started/server-and-client-components">When to use Server and Client Components?</a>.</p>
<h2 id="heading-server-functions">Server Functions</h2>
<p>The corresponding directive <code>‘user server‘</code> is not meant to be the signature of RSCs but rather that of Server Functions, also known as Server Actions. They can be created in Server Components and passed as props to Client Components, or imported into Client Components.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762993131924/dcd1e1bd-440c-4b8b-a4de-82c4c3426148.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-leaves-vs-root-strategy">Leaves vs. Root Strategy</h2>
<p>One of the best practices for improving web application performance is reducing the size of the JS bundle. RSCs reduce JS bundle size by:</p>
<ul>
<li><p>Running only on the server</p>
</li>
<li><p>Not being included in the client-side JavaScript bundle at all</p>
</li>
<li><p>Returning just HTML to the browser</p>
</li>
</ul>
<p>JS bundle for RSCs vs. CSCs:</p>
<ul>
<li><p><strong>Server Components</strong> = Code executes on server → HTML sent to browser → <strong>No JS bundle cost</strong></p>
</li>
<li><p><strong>Client Components</strong> = Code sent to browser → Executes in browser → <strong>Full JS bundle cost</strong></p>
</li>
</ul>
<p>You may wonder about the impact of the HTML output, and in some cases, it might even be larger than the equivalent JavaScript. But there are important differences in how they affect performance:</p>
<p><strong>1. Parse/Execute Cost</strong></p>
<ul>
<li><p>HTML: Browser parses and renders it very efficiently - it's what browsers are optimized for</p>
</li>
<li><p>JavaScript: Must be parsed, compiled, and executed before anything happens. This is CPU-intensive and blocks interactivity</p>
</li>
</ul>
<p><strong>2. When User Can Interact</strong></p>
<ul>
<li><p>HTML: Visible and readable immediately (for non-interactive content)</p>
</li>
<li><p>JS bundle: User waits for download → parse → execute → hydration before page is interactive</p>
</li>
</ul>
<p><strong>3. Main Thread Blocking</strong></p>
<ul>
<li><p>HTML: Doesn't block the main thread</p>
</li>
<li><p>JavaScript: Large bundles block the main thread, making the page feel sluggish</p>
</li>
</ul>
<p>With the understanding of RSCs vs. CSCs, let’s consider these two approaches for a standard ui with a search bar:</p>
<p>❌ Root approach (bad):</p>
<ul>
<li><p>Put <code>'use client'</code> at the Layout level (the root)</p>
</li>
<li><p>All components imported into a Client Component become a Client Component</p>
</li>
<li><p>The entire tree ships to the browser as JavaScript</p>
</li>
</ul>
<p>✓ Leaves approach (good):</p>
<ul>
<li><p>Keep Layout, Header, and Sidebar as Server Components</p>
</li>
<li><p>Only add <code>'use client'</code> to Search and Navigation (the leaves)</p>
</li>
<li><p>Only those small pieces ship as JavaScript</p>
</li>
</ul>
<p>In a tree, leaves are at the edges/endpoints. In your component tree, you want to push <code>'use client'</code> as far out to the edges as possible - to the smallest, most specific components that actually need client-side interactivity.</p>
<p>The closer to the root you place <code>'use client'</code>, the more of your tree becomes client-side code. The closer to the leaves, the less JavaScript you ship.</p>
<h2 id="heading-composition-pattern-vs-rendering">Composition Pattern vs. Rendering</h2>
<p>The most common usecase for context is to set the theme. When you use a Context Provider, you must mark it as a Client Component:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// providers.js</span>
<span class="hljs-string">'use client'</span>  <span class="hljs-comment">// ← Required!</span>

<span class="hljs-keyword">import</span> { createContext } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ThemeContext = createContext();

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ThemeProvider</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ThemeContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"dark"</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ThemeContext.Provider</span>&gt;</span></span>
  );
}
</code></pre>
<p>Even though <code>ThemeProvider</code> is a Client Component, its <code>children</code> can still be Server Components. The Client Component boundary doesn't force everything inside it to become a Client Component—only the provider itself needs to be client-side.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// app/layout.js (Server Component by default)</span>
<span class="hljs-keyword">import</span> { ThemeProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'./providers'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ThemeProvider</span>&gt;</span>  {/* Client Component */}
          {children}      {/* Can still be Server Components! */}
        <span class="hljs-tag">&lt;/<span class="hljs-name">ThemeProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
  );
}
</code></pre>
<p>Children become Client Components only if you <strong>import and render them directly inside</strong> a Client Component:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ClientWrapper.js</span>
<span class="hljs-string">'use client'</span>

<span class="hljs-keyword">import</span> ServerChild <span class="hljs-keyword">from</span> <span class="hljs-string">'./ServerChild'</span>;  <span class="hljs-comment">// ← This import makes it client!</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ClientWrapper</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ServerChild</span> /&gt;</span>  {/* Now it's a Client Component */}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-strategy-to-minimize-bundle-size"><strong>Strategy to Minimize Bundle Size</strong></h2>
<ol>
<li><p>Keep as many components as Server Components as possible</p>
</li>
<li><p>Push <code>'use client'</code> as far down the component tree as you can, or use the composition pattern</p>
</li>
<li><p>Split interactive parts into small Client Components nested within Server Components</p>
</li>
</ol>
<p>Of course, there is a reason for everything, and there is always a tradeoff. In some cases, keeping a component as a Client Component will be more performant. For example:</p>
<ul>
<li><p>Virtual scrolling for huge lists</p>
</li>
<li><p>Interactive dashboards with lots of client-side state</p>
</li>
<li><p>Apps where data changes frequently</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Build a Scalable Full-Stack Next.js App with Drizzle, Neon, Redis, and Vercel]]></title><description><![CDATA[Last week, I attended the live workshop Build a Full-Stack Next.js App v4 by Brian Holt on Frontend Masters. It was an incredible deep dive into building production-grade applications from scratch to deployment. Here is the note I took on crafting a ...]]></description><link>https://uidev.net/build-a-scalable-full-stack-nextjs-app-with-drizzle-neon-redis-and-vercel</link><guid isPermaLink="true">https://uidev.net/build-a-scalable-full-stack-nextjs-app-with-drizzle-neon-redis-and-vercel</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[resend]]></category><category><![CDATA[Redis]]></category><category><![CDATA[Vercel]]></category><category><![CDATA[DrizzleORM]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[neondatabase]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Upstash]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Mon, 03 Nov 2025 21:06:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762120253983/b113fb7d-45c8-4925-96f4-177233508b6f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week, I attended the live workshop <a target="_blank" href="https://frontendmasters.com/workshops/fullstack-app-next-v4/"><strong>Build a Full-Stack Next.js App v4</strong></a> by Brian Holt on Frontend Masters. It was an incredible deep dive into building production-grade applications from scratch to deployment. Here is the note I took on crafting a modern full-stack Next.js application using popular tools.</p>
<hr />
<p><strong>Key Reference Material</strong>:</p>
<ul>
<li><p><a target="_blank" href="https://fullstack-v4.holt.courses/">Build a Fullstack Next.js App, v4 Course Website</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/btholt/fullstack-next-wiki/">Fullstack Next.js Github Repository</a></p>
</li>
<li><p><a target="_blank" href="https://wikimasters.vercel.app">Wikimasters Project Demo</a></p>
</li>
</ul>
<hr />
<p><strong>The Stack</strong></p>
<p>This workshop introduced a curated tech stack optimized for serverless architecture, developer experience, and production reliability:</p>
<ul>
<li><p><strong>Next.js</strong> - The React framework with built-in SSR, SSG, and API routes</p>
</li>
<li><p><strong>TypeScript</strong> - Type safety across the entire application</p>
</li>
<li><p><strong>Neon PostgreSQL</strong> - Serverless Postgres with automatic scaling</p>
</li>
<li><p><strong>Drizzle ORM</strong> - Type-safe, SQL-like database queries</p>
</li>
<li><p><strong>Upstash Redis</strong> - Serverless caching for blazing-fast performance</p>
</li>
<li><p><strong>Vercel</strong> - Seamless deployment and hosting</p>
</li>
<li><p><strong>Resend</strong> - Modern transactional email</p>
</li>
<li><p><strong>GitHub Actions</strong> - Automated CI/CD pipeline</p>
</li>
</ul>
<hr />
<p><strong>Database Layer: Neon (Serverless Postgres) + Drizzle ORM</strong></p>
<ul>
<li><p><a target="_blank" href="https://neon.com/"><strong>Neon</strong></a>:</p>
<p>  Neon's built-in authentication system integrates seamlessly with Neon PostgreSQL databases, providing pre-built tables and middleware for user management. The free tier is incredibly generous because you only pay for actual usage—the database scales up on demand and scales down to zero when idle. Perfect for side projects and MVP development.</p>
</li>
<li><p>Key benefits:</p>
<ul>
<li><p>Built-in authentication system with pre-configured tables</p>
</li>
<li><p>Database branching for development environments</p>
</li>
<li><p>Instant provisioning and automatic backups</p>
</li>
<li><p>Seamless Vercel integration</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://orm.drizzle.team/"><strong>Drizzle ORM</strong></a>:</p>
<p>  <strong>Highly "SQL-ish"</strong>: Unlike ORMs that abstract SQL away, Drizzle keeps SQL logic exposed and accessible. Writing custom queries feels intuitive rather than fighting against the ORM.</p>
<p>  <strong>Deep TypeScript Integration</strong>: Drizzle automatically generates accurate types for your entire schema, including Neon's built-in Auth tables. This saves countless hours of manual type definitions and catches errors at compile time.</p>
<p>  <strong>Lightweight and Fast</strong>: Drizzle has minimal overhead compared to heavier ORMs, resulting in faster query execution and smaller bundle sizes.</p>
<p>  <strong>Creating the Database Schema &amp; Migrating:</strong></p>
<ol>
<li><p>Define your schema in <code>db/schema.ts</code>.</p>
</li>
<li><p>Generate migrations: <code>npx drizzle-kit generate</code></p>
</li>
<li><p>Apply migrations: <code>npx drizzle-kit migrate</code></p>
</li>
<li><p>For instant experimental changes, use: <code>npx drizzle-kit push</code> <em>(applies whatever is in your schema instantly)</em></p>
</li>
</ol>
</li>
</ul>
<p>    <strong>Seeding Data:</strong></p>
<ol>
<li><p>Ensure there's at least one user. (Install <a target="_blank" href="https://npmjs.com/package/tsx"><code>tsx</code></a> for TypeScript-based seeds)</p>
</li>
<li><p>Add seed logic to your data script</p>
</li>
<li><p>Run <code>tsx src/db/seed.ts</code> to seed your database</p>
</li>
</ol>
<hr />
<p><strong>Auth Layer: AuthN (Authentication) &amp; AuthZ (Authorization)</strong></p>
<ul>
<li><p><strong><em>AuthN</em></strong>*:* Confirms user identity; validates who the user is.</p>
</li>
<li><p><strong><em>AuthZ</em></strong>*:* Controls what authenticated users can do (resource permissions).</p>
</li>
<li><p>Setup:</p>
<ul>
<li>Implement backend logic on <code>authz.ts</code> within your <code>db/</code> folder—allow logged-in users to edit their own articles based on role and permissions.</li>
</ul>
</li>
</ul>
<hr />
<p><strong>File Storage for Image Upload with Vercel</strong></p>
<ol>
<li><p>Link your Next.js app repo to <a target="_blank" href="https://vercel.com/">Vercel</a>.</p>
</li>
<li><p>Configure a blob storage in Vercel and add the required environment variables in <code>next.config.js</code>.</p>
</li>
<li><p>Reference the storage (image URL) in <code>drizzle.config.ts</code>.</p>
</li>
<li><p>Use Vercel's upload API for image input in <code>upload.ts</code>.</p>
</li>
</ol>
<hr />
<p><strong>Caching with Upstash Redis</strong></p>
<p>Redis provides sub-millisecond response times—orders of magnitude faster than database queries. For frequently accessed, infrequently changed data (such as article listings or user profiles), caching dramatically improves the user experience and reduces database load.</p>
<p><a target="_blank" href="https://upstash.com/">Upstash</a> makes Redis serverless with per-request pricing, so you're not paying for idle time.</p>
<ol>
<li><p>Create your Redis project on <strong>Upstash</strong>.</p>
</li>
<li><p>Add credentials to your <code>.env</code>.</p>
</li>
<li><p>Add cache logic to <code>lib/data/articles.ts</code>.</p>
</li>
</ol>
<hr />
<p><strong>Email Support via Resend</strong></p>
<p><a target="_blank" href="https://resend.com/">Resend</a> <strong>i</strong>s a developer-friendly email API that makes transactional emails painless:</p>
<ol>
<li><p>Set up a custom domain (DNS records) on <strong>Resend</strong>.</p>
</li>
<li><p>Configure sender/domain options.</p>
</li>
<li><p>Create/send templated emails (e.g., celebration notice).</p>
</li>
<li><p>Send a notification when the page view count passes a threshold (e.g., &gt;10 views triggers an email).</p>
</li>
</ol>
<hr />
<p><strong>CI/CD Pipeline with Vercel &amp; Neon</strong></p>
<ol>
<li><p>Add Neon integration on Vercel’s dashboard.</p>
</li>
<li><p>Add current Vercel deployment URL to Neon (especially for unique/ephemeral URL management).</p>
</li>
<li><p>Manage environment variables for staging, production, and preview branches.</p>
</li>
<li><p>Separate blob storage by environment for isolation and reliability.</p>
</li>
<li><p>Vercel provides built-in analytics and speed insights (free for one project per account):</p>
<ul>
<li><p>Real-time performance monitoring</p>
</li>
<li><p>Bot protection and firewall</p>
</li>
<li><p>Core Web Vitals tracking</p>
</li>
</ul>
</li>
</ol>
<hr />
<p><strong>Github Actions and Testing</strong></p>
<p>Automate your workflow:</p>
<ul>
<li><p>Run <code>lint</code> → <code>typecheck</code> → <code>test:ci</code> (unit tests) → <code>test:e2e:ci</code> → deploy to production.</p>
</li>
<li><p>Use GitHub secrets for per-env variables; copy carefully from Vercel except Neon (generate that API key on Neon).</p>
</li>
<li><p>Always isolate test environments—a shared cache or DB may yield false positives/failures.</p>
</li>
</ul>
<hr />
<p><strong>Best Practices &amp; Key Takeaways</strong></p>
<p><strong>Database &amp; Caching</strong></p>
<ul>
<li><p>Use Redis exclusively for performance optimization, not data persistence—always persist to database periodically.</p>
</li>
<li><p>Monitor cache hit rates to validate your caching strategy</p>
</li>
</ul>
<p><strong>Testing &amp; CI/CD</strong></p>
<ul>
<li><p>Isolate test environments completely to avoid cross-contamination between staging and production.</p>
</li>
<li><p>Set up GitHub Actions to run the full test suite in CI before deploying</p>
</li>
</ul>
<p><strong>Deployment &amp; Monitoring</strong></p>
<ul>
<li><p>Automate domain cleanup if deployment URLs rotate frequently</p>
</li>
<li><p>Use Vercel's analytics to monitor real-world performance and identify bottlenecks</p>
</li>
<li><p>Enable bot protection to prevent spam</p>
</li>
</ul>
<p><strong>Developer Experience</strong></p>
<ul>
<li><p>Drizzle's TypeScript integration catches errors at compile time, saving debugging time in production</p>
</li>
<li><p>Neon's database branching allows safe experimentation without affecting production data</p>
</li>
<li><p>Serverless architecture means you're not paying for idle resources</p>
</li>
<li><p>Vercel integrates seamlessly with different services with key management</p>
</li>
</ul>
<hr />
<p>By combining Drizzle’s type-safe ORM, Neon’s flexible Postgres, blazing-fast caching with Upstash Redis, and modern deployment with Vercel, you can ship highly scalable Next.js apps with confidence and speed.</p>
]]></content:encoded></item><item><title><![CDATA[Setting Up a Modern Monorepo with NX]]></title><description><![CDATA[Introduction
Managing multiple related projects can quickly become overwhelming when scattered across different repositories. NX offers a powerful solution for organizing React applications, shared libraries, and backend services in a single, well-st...]]></description><link>https://uidev.net/setting-up-a-modern-monorepo-with-nx</link><guid isPermaLink="true">https://uidev.net/setting-up-a-modern-monorepo-with-nx</guid><category><![CDATA[monorepo]]></category><category><![CDATA[Nx]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Wed, 22 Oct 2025 22:04:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HLQDfaJUTVI/upload/ad7976f9594d7331dea8aaabe12f40df.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Managing multiple related projects can quickly become overwhelming when scattered across different repositories. NX offers a powerful solution for organizing React applications, shared libraries, and backend services in a single, well-structured monorepo. This guide walks you through setting up a production-ready NX workspace with modern best practices.</p>
<h2 id="heading-why-choose-nx-for-your-monorepo">Why Choose NX for Your Monorepo?</h2>
<p>NX has evolved significantly and now offers compelling advantages for development teams:</p>
<ol>
<li><p><strong>Intelligent Caching</strong>: NX automatically caches task results locally and remotely, dramatically reducing build and test times</p>
</li>
<li><p><strong>Smart Builds</strong>: Only rebuilds projects affected by your changes, saving significant CI/CD time</p>
</li>
<li><p><strong>Dependency Visualization</strong>: Interactive dependency graphs help you understand your codebase architecture</p>
</li>
<li><p><strong>Modern Tooling</strong>: Built-in support for Jest, Vitest, Cypress, Playwright, and Storybook</p>
</li>
<li><p><strong>Framework Agnostic</strong>: Supports React, Angular, Vue, Node.js, and many other frameworks</p>
</li>
<li><p><strong>Code Generation</strong>: Powerful generators create consistent, well-structured code across your workspace</p>
</li>
<li><p><strong>Nx Cloud Integration</strong>: Optional remote caching and distributed task execution for enterprise teams</p>
</li>
</ol>
<h2 id="heading-getting-started">Getting Started</h2>
<h3 id="heading-1-choose-your-monorepo-strategy">1. Choose Your Monorepo Strategy</h3>
<p>NX offers two approaches for organizing monorepos: <strong>Integrated</strong> and <strong>Package-Based</strong>.</p>
<p><strong>Integrated Approach (Recommended for Most Teams)</strong>:</p>
<ul>
<li><p>All dependencies in root <code>package.json</code></p>
</li>
<li><p>Single version policy across the workspace</p>
</li>
<li><p>Faster setup for new projects</p>
</li>
<li><p>Easier dependency management</p>
</li>
<li><p>Better for teams working on interconnected projects</p>
</li>
</ul>
<p><strong>Package-Based Approach</strong>:</p>
<ul>
<li><p>More suitable if you need different dependencies and tooling between applications</p>
</li>
<li><p>Each project can have its own <code>package.json</code> with specific versions</p>
</li>
<li><p>Better for migrating existing projects</p>
</li>
<li><p>More flexibility but increased complexity</p>
</li>
<li><p>Ideal for independent apps with different React versions or tech stacks</p>
</li>
</ul>
<h3 id="heading-2-create-your-nx-workspace">2. Create Your NX Workspace</h3>
<p>The latest version of NX (v21+) has streamlined workspace creation:</p>
<pre><code class="lang-bash">npx create-nx-workspace@latest my-workspace
</code></pre>
<p>During setup, you'll be prompted to choose:</p>
<ul>
<li><p><strong>Preset</strong>: Select <code>react-monorepo</code> for a React-focused workspace</p>
</li>
<li><p><strong>Bundler</strong>: Choose <code>vite</code> (recommended) or <code>webpack</code></p>
</li>
<li><p><strong>Test Runner</strong>: Select <code>vitest</code> (modern) or <code>jest</code></p>
</li>
<li><p><strong>Nx Cloud</strong>: Enable for remote caching (recommended for teams)</p>
</li>
</ul>
<p>For an <strong>integrated workspace</strong> (default), your structure will look like:</p>
<pre><code class="lang-javascript">my-workspace/
├── apps/              # Applications live here
├── libs/              # Shared libraries
├── nx.json            # NX configuration
├── package.json       # Single source <span class="hljs-keyword">of</span> truth <span class="hljs-keyword">for</span> dependencies
└── tsconfig.base.json # Path mappings <span class="hljs-keyword">for</span> imports
</code></pre>
<p>For a <strong>package-based workspace</strong>, each app/lib will have its own <code>package.json</code>.</p>
<h3 id="heading-2-generate-a-react-application">2. Generate a React Application</h3>
<p>The <code>@nx/react</code> plugin (formerly <code>@nrwl/react</code>) provides updated generators:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install the React plugin (if not already installed)</span>
npm install --save-dev @nx/react

<span class="hljs-comment"># Generate a React application</span>
npx nx generate @nx/react:application my-app --style=scss --bundler=vite
</code></pre>
<h3 id="heading-3-managing-multiple-react-apps-with-different-versions">3. Managing Multiple React Apps with Different Versions</h3>
<p>One of the challenges in a monorepo is managing multiple apps that may need different versions of dependencies. Here's how to handle this:</p>
<h4 id="heading-option-1-using-npm-aliases-integrated-approach">Option 1: Using NPM Aliases (Integrated Approach)</h4>
<p>If you need to support different versions of React in the same workspace, you can use NPM aliases:</p>
<pre><code class="lang-json"><span class="hljs-comment">// Root package.json</span>
{
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"react-18"</span>: <span class="hljs-string">"npm:react@^18.2.0"</span>,
    <span class="hljs-attr">"react-dom-18"</span>: <span class="hljs-string">"npm:react-dom@^18.2.0"</span>
  }
}
</code></pre>
<p>Then configure your legacy app's <code>tsconfig.json</code> to map to the older version:</p>
<pre><code class="lang-json"><span class="hljs-comment">// apps/legacy-app/tsconfig.json</span>
{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"paths"</span>: {
      <span class="hljs-attr">"react"</span>: [<span class="hljs-string">"../../node_modules/react-18"</span>],
      <span class="hljs-attr">"react-dom"</span>: [<span class="hljs-string">"../../node_modules/react-dom-18"</span>]
    }
  }
}
</code></pre>
<h4 id="heading-option-2-package-based-workspace-recommended-for-different-versions">Option 2: Package-Based Workspace (Recommended for Different Versions)</h4>
<p>For scenarios where apps truly need different dependency versions, use a package-based repository which is more suitable for having different dependencies and tooling between applications:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a package-based workspace</span>
npx create-nx-workspace@latest my-workspace --preset=npm
</code></pre>
<p>In this setup, each application has its own <code>package.json</code>:</p>
<pre><code class="lang-javascript">my-workspace/
├── packages/
│   ├── modern-app/
│   │   ├── package.json      # React <span class="hljs-number">19</span>
│   │   └── src/
│   └── legacy-app/
│       ├── package.json      # React <span class="hljs-number">18</span>
│       └── src/
└── package.json              # Root <span class="hljs-keyword">for</span> shared dev dependencies
</code></pre>
<p><strong>Modern App</strong> (<code>packages/modern-app/package.json</code>):</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"@my-workspace/modern-app"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"@my-workspace/shared-ui-v3"</span>: <span class="hljs-string">"*"</span>
  }
}
</code></pre>
<p><strong>Legacy App</strong> (<code>packages/legacy-app/package.json</code>):</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"@my-workspace/legacy-app"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"2.5.0"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-attr">"lodash"</span>: <span class="hljs-string">"^4.17.0"</span>,
    <span class="hljs-attr">"@my-workspace/shared-ui-legacy"</span>: <span class="hljs-string">"*"</span>
  }
}
</code></pre>
<h4 id="heading-option-3-per-project-dependency-overrides-integrated-approach">Option 3: Per-Project Dependency Overrides (Integrated Approach)</h4>
<p>Even with an integrated approach, you can enable project-level dependency overrides. Create a <code>package.json</code> in your specific app:</p>
<pre><code class="lang-json"><span class="hljs-comment">// apps/legacy-app/package.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"legacy-app"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"18.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"18.2.0"</span>
  }
}
</code></pre>
<p>Then configure your package manager (pnpm example) to use these overrides:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># pnpm-workspace.yaml</span>
<span class="hljs-attr">packages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">'apps/*'</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">'libs/*'</span>
</code></pre>
<p><strong>Important Considerations:</strong></p>
<ul>
<li><p>If projects use different versions of React, runtime issues can occur when sharing components between projects</p>
</li>
<li><p>Maintain separate component libraries for each major version</p>
</li>
<li><p>Use caution when sharing code between apps with different dependency versions</p>
</li>
<li><p>React 19 introduced major features like Server Components and the Actions API, which may not be compatible with older React versions</p>
</li>
</ul>
<h3 id="heading-3-create-shared-libraries">3. Create Shared Libraries</h3>
<p>Libraries enable code sharing across applications:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Generate a UI component library</span>
npx nx generate @nx/react:library ui-components --directory=libs/shared --style=scss --bundler=vite

<span class="hljs-comment"># Generate a utilities library</span>
npx nx generate @nx/react:library utils --directory=libs/shared --bundler=none
</code></pre>
<p><strong>Key Options:</strong></p>
<ul>
<li><p><code>--directory</code>: Organizes libraries into folders</p>
</li>
<li><p><code>--style</code>: CSS/SCSS/styled-components/emotion</p>
</li>
<li><p><code>--bundler</code>: <code>vite</code>, <code>rollup</code>, or <code>none</code> (for non-buildable libraries)</p>
</li>
<li><p><code>--unitTestRunner</code>: <code>vitest</code>, <code>jest</code>, or <code>none</code></p>
</li>
</ul>
<p><strong>Creating Version-Specific Libraries:</strong></p>
<p>When managing apps with different React versions, create separate libraries for each version:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Modern UI library (React 19)</span>
npx nx generate @nx/react:library ui-modern --directory=libs/shared --style=scss

<span class="hljs-comment"># Legacy UI library (React 18)</span>
npx nx generate @nx/react:library ui-legacy --directory=libs/shared --style=scss
</code></pre>
<p>Configure library dependencies in <code>package.json</code> (package-based) or use peer dependencies:</p>
<pre><code class="lang-json"><span class="hljs-comment">// libs/shared/ui-modern/package.json</span>
{
  <span class="hljs-attr">"peerDependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^19.0.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^19.0.0"</span>
  }
}

<span class="hljs-comment">// libs/shared/ui-legacy/package.json</span>
{
  <span class="hljs-attr">"peerDependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^18.0.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^18.0.0"</span>
  }
}
</code></pre>
<h3 id="heading-4-generate-components">4. Generate Components</h3>
<p>Create components within your libraries:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Generate a component in a library</span>
npx nx generate @nx/react:component Button --project=ui-components --directory=src/lib

<span class="hljs-comment"># Generate a component with export</span>
npx nx generate @nx/react:component Header --project=ui-components --<span class="hljs-built_in">export</span>
</code></pre>
<p>The component will be generated with:</p>
<ul>
<li><p>Component file (<code>.tsx</code>)</p>
</li>
<li><p>Style file (<code>.scss</code>)</p>
</li>
<li><p>Test file (<code>.spec.tsx</code>)</p>
</li>
<li><p>Automatic export from library's index file (if <code>--export</code> is used)</p>
</li>
</ul>
<h2 id="heading-working-with-state-management">Working with State Management</h2>
<h3 id="heading-redux-toolkit">Redux Toolkit</h3>
<p>NX provides a Redux slice generator:</p>
<pre><code class="lang-bash">npx nx generate @nx/react:redux todos --project=my-app
</code></pre>
<p>This generates:</p>
<ul>
<li><p>Redux slice with TypeScript</p>
</li>
<li><p>Configured store setup</p>
</li>
<li><p>Test files</p>
</li>
</ul>
<p>For shared state across apps, create a dedicated state library:</p>
<pre><code class="lang-bash">npx nx generate @nx/react:library state --directory=libs/shared
npx nx generate @nx/react:redux users --project=state
</code></pre>
<h2 id="heading-sharing-assets-and-styles">Sharing Assets and Styles</h2>
<h3 id="heading-global-styles-library">Global Styles Library</h3>
<p>Create a centralized styles library:</p>
<pre><code class="lang-bash">npx nx generate @nx/react:library styles --directory=libs/shared --style=scss --bundler=none
</code></pre>
<p>Configure in your app's <code>project.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"targets"</span>: {
    <span class="hljs-attr">"build"</span>: {
      <span class="hljs-attr">"options"</span>: {
        <span class="hljs-attr">"styles"</span>: [<span class="hljs-string">"libs/shared/styles/src/lib/global.scss"</span>],
        <span class="hljs-attr">"stylePreprocessorOptions"</span>: {
          <span class="hljs-attr">"includePaths"</span>: [<span class="hljs-string">"libs/shared/styles/src/lib"</span>]
        }
      }
    }
  }
}
</code></pre>
<p>Then import in your components:</p>
<pre><code class="lang-scss"><span class="hljs-keyword">@import</span> <span class="hljs-string">"variables"</span>;
<span class="hljs-keyword">@import</span> <span class="hljs-string">"mixins"</span>;

<span class="hljs-selector-class">.my-component</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-variable">$primary-color</span>;
}
</code></pre>
<h3 id="heading-shared-assets">Shared Assets</h3>
<p>Configure assets in <code>project.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"targets"</span>: {
    <span class="hljs-attr">"build"</span>: {
      <span class="hljs-attr">"options"</span>: {
        <span class="hljs-attr">"assets"</span>: [
          <span class="hljs-string">"apps/my-app/src/favicon.ico"</span>,
          <span class="hljs-string">"apps/my-app/src/assets"</span>,
          {
            <span class="hljs-attr">"input"</span>: <span class="hljs-string">"libs/shared/assets/src"</span>,
            <span class="hljs-attr">"glob"</span>: <span class="hljs-string">"**/*"</span>,
            <span class="hljs-attr">"output"</span>: <span class="hljs-string">"assets"</span>
          }
        ]
      }
    }
  }
}
</code></pre>
<h2 id="heading-testing-configuration">Testing Configuration</h2>
<h3 id="heading-unit-testing-with-vitest-recommended">Unit Testing with Vitest (Recommended)</h3>
<p>Modern NX workspaces use Vitest by default:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run tests for a specific project</span>
npx nx <span class="hljs-built_in">test</span> my-app

<span class="hljs-comment"># Run tests for all projects</span>
npx nx run-many --target=<span class="hljs-built_in">test</span>

<span class="hljs-comment"># Run tests only for affected projects</span>
npx nx affected --target=<span class="hljs-built_in">test</span>
</code></pre>
<p>Configure coverage in <code>vitest.config.ts</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest/config'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">coverage</span>: {
      <span class="hljs-attr">provider</span>: <span class="hljs-string">'v8'</span>,
      <span class="hljs-attr">reporter</span>: [<span class="hljs-string">'text'</span>, <span class="hljs-string">'html'</span>, <span class="hljs-string">'lcov'</span>],
      <span class="hljs-attr">thresholds</span>: {
        <span class="hljs-attr">branches</span>: <span class="hljs-number">80</span>,
        <span class="hljs-attr">functions</span>: <span class="hljs-number">80</span>,
        <span class="hljs-attr">lines</span>: <span class="hljs-number">80</span>,
        <span class="hljs-attr">statements</span>: <span class="hljs-number">80</span>
      }
    }
  }
});
</code></pre>
<h3 id="heading-e2e-testing-with-cypress">E2E Testing with Cypress</h3>
<p>Generate E2E tests with Cypress:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Configure Cypress for an application</span>
npx nx generate @nx/cypress:configuration --project=my-app

<span class="hljs-comment"># Run Cypress tests</span>
npx nx e2e my-app-e2e

<span class="hljs-comment"># Run Cypress in interactive mode</span>
npx nx e2e my-app-e2e --watch
</code></pre>
<p>Cypress tests are automatically generated in a separate e2e project:</p>
<pre><code class="lang-javascript">my-workspace/
├── apps/
│   ├── my-app/
│   └── my-app-e2e/              # Cypress E2E tests
│       ├── src/
│       │   ├── e2e/
│       │   │   └── app.cy.ts
│       │   ├── fixtures/
│       │   └── support/
│       ├── cypress.config.ts
│       └── project.json
</code></pre>
<h2 id="heading-storybook-integration">Storybook Integration</h2>
<p>Set up Storybook for component development:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Configure Storybook for a library</span>
npx nx generate @nx/react:storybook-configuration ui-components

<span class="hljs-comment"># Run Storybook</span>
npx nx storybook ui-components

<span class="hljs-comment"># Generate stories for all components</span>
npx nx generate @nx/react:stories ui-components
</code></pre>
<p><strong>Modern Storybook Setup (v7+):</strong></p>
<ul>
<li><p>Uses Vite for faster builds</p>
</li>
<li><p>Component Story Format 3 (CSF3)</p>
</li>
<li><p>Automatic story generation</p>
</li>
<li><p>Interaction testing support</p>
</li>
</ul>
<h2 id="heading-building-and-deployment">Building and Deployment</h2>
<h3 id="heading-production-build">Production Build</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Build a specific app</span>
npx nx build my-app --configuration=production

<span class="hljs-comment"># Build all apps</span>
npx nx run-many --target=build --configuration=production

<span class="hljs-comment"># Build only affected projects</span>
npx nx affected --target=build --configuration=production
</code></pre>
<h3 id="heading-build-optimization">Build Optimization</h3>
<p>Configure in <code>project.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"targets"</span>: {
    <span class="hljs-attr">"build"</span>: {
      <span class="hljs-attr">"configurations"</span>: {
        <span class="hljs-attr">"production"</span>: {
          <span class="hljs-attr">"optimization"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">"extractCss"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-attr">"namedChunks"</span>: <span class="hljs-literal">false</span>
        }
      }
    }
  }
}
</code></pre>
<h2 id="heading-essential-nx-commands">Essential NX Commands</h2>
<h3 id="heading-workspace-operations">Workspace Operations</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># View project dependency graph</span>
npx nx graph

<span class="hljs-comment"># List all projects</span>
npx nx show projects

<span class="hljs-comment"># Show project details</span>
npx nx show project my-app

<span class="hljs-comment"># Run linting</span>
npx nx lint my-app

<span class="hljs-comment"># Format code</span>
npx nx format:write
</code></pre>
<h3 id="heading-affected-commands">Affected Commands</h3>
<p>NX's killer feature - only work on what changed:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Show affected projects</span>
npx nx affected:graph

<span class="hljs-comment"># Test affected projects</span>
npx nx affected --target=<span class="hljs-built_in">test</span>

<span class="hljs-comment"># Build affected projects</span>
npx nx affected --target=build

<span class="hljs-comment"># Lint affected projects</span>
npx nx affected --target=lint
</code></pre>
<h3 id="heading-removing-projects">Removing Projects</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Remove a project</span>
npx nx generate @nx/workspace:remove my-old-app

<span class="hljs-comment"># Dry run first</span>
npx nx generate @nx/workspace:remove my-old-app --dry-run
</code></pre>
<h2 id="heading-path-mapping-and-imports">Path Mapping and Imports</h2>
<p>NX configures TypeScript path mappings in <code>tsconfig.base.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"paths"</span>: {
      <span class="hljs-attr">"@my-workspace/ui-components"</span>: [<span class="hljs-string">"libs/shared/ui-components/src/index.ts"</span>],
      <span class="hljs-attr">"@my-workspace/utils"</span>: [<span class="hljs-string">"libs/shared/utils/src/index.ts"</span>],
      <span class="hljs-attr">"@my-workspace/state"</span>: [<span class="hljs-string">"libs/shared/state/src/index.ts"</span>]
    }
  }
}
</code></pre>
<p>Import like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'@my-workspace/ui-components'</span>;
<span class="hljs-keyword">import</span> { formatDate } <span class="hljs-keyword">from</span> <span class="hljs-string">'@my-workspace/utils'</span>;
</code></pre>
<h2 id="heading-cicd-integration">CI/CD Integration</h2>
<p>NX automatically generates GitHub Actions configuration:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/ci.yml</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CI</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">0</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node.js</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-number">20</span>
          <span class="hljs-attr">cache:</span> <span class="hljs-string">'npm'</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">nrwl/nx-set-shas@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">nx</span> <span class="hljs-string">affected</span> <span class="hljs-string">--target=lint</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">nx</span> <span class="hljs-string">affected</span> <span class="hljs-string">--target=test</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">nx</span> <span class="hljs-string">affected</span> <span class="hljs-string">--target=build</span>
</code></pre>
<h2 id="heading-performance-best-practices">Performance Best Practices</h2>
<ol>
<li><p><strong>Enable Nx Cloud</strong>: Provides remote caching and distributed task execution</p>
</li>
<li><p><strong>Use Affected Commands</strong>: Only build/test what changed</p>
</li>
<li><p><strong>Configure Caching</strong>: Ensure all targets are cacheable in <code>nx.json</code></p>
</li>
<li><p><strong>Use Vite</strong>: Much faster than webpack for development and builds</p>
</li>
<li><p><strong>Parallel Execution</strong>: NX automatically runs tasks in parallel when possible</p>
</li>
<li><p><strong>Choose the Right Strategy</strong>:</p>
<ul>
<li><p>Use <strong>integrated</strong> for better performance and simpler dependency management</p>
</li>
<li><p>Use <strong>package-based</strong> only when you truly need different dependency versions across apps</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-dependency-management-strategies-comparison">Dependency Management Strategies Comparison</h2>
<p>NX fully supports both dependency management strategies, and you can even mix these strategies, using a single version policy for most dependencies while allowing specific projects to maintain their own versions when necessary.</p>
<h3 id="heading-single-version-policy-integrated-recommended">Single Version Policy (Integrated - Recommended)</h3>
<p><strong>Pros:</strong></p>
<ul>
<li><p>✅ Simpler dependency management</p>
</li>
<li><p>✅ Better caching and performance</p>
</li>
<li><p>✅ Reduces duplication and conflicts</p>
</li>
<li><p>✅ Easier upgrades (update once, affects all)</p>
</li>
<li><p>✅ Guaranteed compatibility between shared libraries</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>❌ All apps must coordinate on dependency versions</p>
</li>
<li><p>❌ Breaking changes affect entire workspace</p>
</li>
<li><p>❌ May need to keep older apps on older framework versions</p>
</li>
</ul>
<h3 id="heading-independent-dependencies-package-based">Independent Dependencies (Package-Based)</h3>
<p><strong>Pros:</strong></p>
<ul>
<li><p>✅ Each app/package controls its own dependencies</p>
</li>
<li><p>✅ Easier to migrate existing projects</p>
</li>
<li><p>✅ Can run different React versions simultaneously</p>
</li>
<li><p>✅ Individual apps can upgrade independently</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>❌ More complex dependency management</p>
</li>
<li><p>❌ Potential version conflicts when sharing code</p>
</li>
<li><p>❌ Larger total <code>node_modules</code> size</p>
</li>
<li><p>❌ Harder to ensure compatibility</p>
</li>
<li><p>❌ Runtime errors when sharing components between different React versions</p>
</li>
</ul>
<h3 id="heading-when-to-use-each-approach">When to Use Each Approach</h3>
<p><strong>Use Integrated (Single Version) When:</strong></p>
<ul>
<li><p>All apps are actively maintained</p>
</li>
<li><p>Teams can coordinate on upgrades</p>
</li>
<li><p>Apps share significant code</p>
</li>
<li><p>Performance and caching are priorities</p>
</li>
<li><p>Starting a new monorepo from scratch</p>
</li>
</ul>
<p><strong>Use Package-Based (Multiple Versions) When:</strong></p>
<ul>
<li><p>Migrating existing independent projects</p>
</li>
<li><p>Apps have vastly different technology needs</p>
</li>
<li><p>You need to support legacy apps alongside modern ones</p>
</li>
<li><p>Different teams own different apps with minimal overlap</p>
</li>
<li><p>Apps are published as independent NPM packages</p>
</li>
</ul>
<h2 id="heading-key-benefits-summary">Key Benefits Summary</h2>
<ul>
<li><p>✅ <strong>Test Components in Isolation</strong>: Storybook integration for component development</p>
</li>
<li><p>✅ <strong>Easy Code Sharing</strong>: Import libraries with clean TypeScript paths</p>
</li>
<li><p>✅ <strong>Modern Tooling</strong>: Vite, Vitest, Cypress, ESLint, Prettier pre-configured</p>
</li>
<li><p>✅ <strong>CLI Generators</strong>: Bootstrap apps, libraries, and components with consistent structure</p>
</li>
<li><p>✅ <strong>Visual Dependency Graph</strong>: Understand project relationships at a glance</p>
</li>
<li><p>✅ <strong>Smart CI/CD</strong>: Only test and build affected projects</p>
</li>
<li><p>✅ <strong>Framework Flexibility</strong>: React, Node.js, and many other frameworks in one repo</p>
</li>
<li><p>✅ <strong>Type Safety</strong>: Full TypeScript support across the workspace</p>
</li>
</ul>
<h2 id="heading-migration-notes-from-legacy-nx">Migration Notes from Legacy NX</h2>
<p>If updating from older NX versions:</p>
<ol>
<li><p><strong>Package Names</strong>: <code>@nrwl/*</code> → <code>@nx/*</code></p>
</li>
<li><p><strong>Commands</strong>: <code>nx affected:test</code> → <code>nx affected --target=test</code></p>
</li>
<li><p><strong>Bundlers</strong>: Consider migrating from webpack to Vite</p>
</li>
<li><p><strong>Test Runners</strong>: Consider migrating from Jest to Vitest</p>
</li>
<li><p><strong>Configuration</strong>: <code>workspace.json</code> deprecated, use <code>project.json</code> files</p>
</li>
</ol>
<p>Run the migration command:</p>
<pre><code class="lang-bash">npx nx migrate latest
npx nx migrate --run-migrations
</code></pre>
<h2 id="heading-final-repository-structure">Final Repository Structure</h2>
<p>After following this guide, here are two example structures depending on your approach:</p>
<h3 id="heading-integrated-workspace-structure-single-version-policy">Integrated Workspace Structure (Single Version Policy)</h3>
<pre><code class="lang-javascript">my-workspace/
├── apps/
│   ├── customer-portal/                 # Modern React <span class="hljs-number">19</span> app
│   │   ├── src/
│   │   │   ├── app/
│   │   │   │   ├── app.tsx
│   │   │   │   ├── app.module.scss
│   │   │   │   └── redux/
│   │   │   ├── main.tsx
│   │   │   ├── assets/
│   │   │   └── styles.scss
│   │   ├── project.json
│   │   ├── vite.config.ts
│   │   └── tsconfig.json
│   │
│   ├── customer-portal-e2e/
│   │   ├── src/
│   │   │   ├── e2e/
│   │   │   │   └── app.cy.ts
│   │   │   └── support/
│   │   ├── cypress.config.ts
│   │   └── project.json
│   │
│   └── admin-dashboard/                 # Another React <span class="hljs-number">19</span> app
│       ├── src/
│       ├── project.json
│       └── vite.config.ts
│
├── libs/
│   └── shared/
│       ├── ui-components/              # Shared React components
│       │   ├── src/
│       │   │   ├── index.ts
│       │   │   └── lib/
│       │   │       ├── button/
│       │   │       ├── header/
│       │   │       └── card/
│       │   ├── .storybook/
│       │   ├── project.json
│       │   └── vite.config.ts
│       │
│       ├── utils/                      # Shared utilities
│       │   ├── src/
│       │   │   ├── index.ts
│       │   │   └── lib/
│       │   │       ├── date-utils.ts
│       │   │       └── string-utils.ts
│       │   └── project.json
│       │
│       ├── state/                      # Shared Redux state
│       │   ├── src/
│       │   │   ├── index.ts
│       │   │   └── lib/
│       │   │       ├── store.ts
│       │   │       └── slices/
│       │   └── project.json
│       │
│       ├── styles/                     # Shared SCSS
│       │   ├── src/
│       │   │   └── lib/
│       │   │       ├── _variables.scss
│       │   │       ├── _mixins.scss
│       │   │       └── <span class="hljs-built_in">global</span>.scss
│       │   └── project.json
│       │
│       └── assets/                     # Shared images, icons
│           └── src/
│
├── .github/
│   └── workflows/
│       └── ci.yml
│
├── node_modules/
├── dist/
├── nx.json
├── package.json                        # Single source <span class="hljs-keyword">for</span> all dependencies
├── tsconfig.base.json
└── README.md
</code></pre>
<h3 id="heading-package-based-structure-multiple-versions-support">Package-Based Structure (Multiple Versions Support)</h3>
<p>Best for scenarios where apps need different React versions or independent dependencies:</p>
<pre><code class="lang-javascript">my-workspace/
├── packages/
│   ├── modern-app/                     # React <span class="hljs-number">19</span> application
│   │   ├── src/
│   │   │   ├── app/
│   │   │   └── main.tsx
│   │   ├── package.json                # React <span class="hljs-number">19.2</span><span class="hljs-number">.0</span>
│   │   ├── project.json
│   │   ├── vite.config.ts
│   │   └── node_modules/               # App-specific deps
│   │
│   ├── legacy-app/                     # React <span class="hljs-number">18</span> application
│   │   ├── src/
│   │   │   ├── app/
│   │   │   └── main.tsx
│   │   ├── package.json                # React <span class="hljs-number">18.2</span><span class="hljs-number">.0</span>, lodash <span class="hljs-number">4.17</span><span class="hljs-number">.0</span>
│   │   ├── project.json
│   │   ├── webpack.config.js
│   │   └── node_modules/
│   │
│   ├── admin-portal/                   # React <span class="hljs-number">19</span> <span class="hljs-keyword">with</span> different packages
│   │   ├── src/
│   │   ├── package.json                # React <span class="hljs-number">19.2</span><span class="hljs-number">.0</span>, Material-UI <span class="hljs-number">6</span>
│   │   ├── project.json
│   │   └── node_modules/
│   │
│   ├── ui-modern/                      # UI lib <span class="hljs-keyword">for</span> React <span class="hljs-number">19</span>
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   │       ├── Button/
│   │   │       └── Card/
│   │   ├── package.json                # peerDeps: React <span class="hljs-number">19</span>
│   │   ├── project.json
│   │   └── node_modules/
│   │
│   ├── ui-legacy/                      # UI lib <span class="hljs-keyword">for</span> React <span class="hljs-number">18</span>
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   ├── package.json                # peerDeps: React <span class="hljs-number">18</span>
│   │   ├── project.json
│   │   └── node_modules/
│   │
│   ├── shared-utils/                   # Framework-agnostic utils
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   ├── package.json                # No React dependency
│   │   └── project.json
│   │
│   └── shared-types/                   # TypeScript types
│       ├── src/
│       │   └── index.ts
│       ├── package.json
│       └── project.json
│
├── .github/
│   └── workflows/
│       └── ci.yml
│
├── node_modules/                       # Shared dev dependencies only
├── dist/
├── nx.json
├── package.json                        # Root-level dev dependencies
├── pnpm-workspace.yaml                 # or yarn workspaces config
├── tsconfig.base.json
└── README.md
</code></pre>
<h3 id="heading-real-world-example-multi-version-dependency-configuration">Real-World Example: Multi-Version Dependency Configuration</h3>
<p><strong>Root</strong> <code>package.json</code> (Package-Based):</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"my-workspace"</span>,
  <span class="hljs-attr">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"workspaces"</span>: [
    <span class="hljs-string">"packages/*"</span>
  ],
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@nx/react"</span>: <span class="hljs-string">"^21.6.4"</span>,
    <span class="hljs-attr">"@nx/vite"</span>: <span class="hljs-string">"^21.6.4"</span>,
    <span class="hljs-attr">"@nx/webpack"</span>: <span class="hljs-string">"^21.6.4"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^5.3.0"</span>,
    <span class="hljs-attr">"vitest"</span>: <span class="hljs-string">"^1.0.0"</span>,
    <span class="hljs-attr">"eslint"</span>: <span class="hljs-string">"^8.55.0"</span>,
    <span class="hljs-attr">"prettier"</span>: <span class="hljs-string">"^3.1.0"</span>
  }
}
</code></pre>
<p><strong>Modern App</strong> <code>package.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"@my-workspace/modern-app"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"3.0.0"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"react-router-dom"</span>: <span class="hljs-string">"^6.20.0"</span>,
    <span class="hljs-attr">"@tanstack/react-query"</span>: <span class="hljs-string">"^5.0.0"</span>,
    <span class="hljs-attr">"@my-workspace/ui-modern"</span>: <span class="hljs-string">"*"</span>,
    <span class="hljs-attr">"@my-workspace/shared-utils"</span>: <span class="hljs-string">"*"</span>
  }
}
</code></pre>
<p><strong>Legacy App</strong> <code>package.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"@my-workspace/legacy-app"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"2.14.5"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-attr">"react-router-dom"</span>: <span class="hljs-string">"^6.20.0"</span>,
    <span class="hljs-attr">"redux"</span>: <span class="hljs-string">"^4.2.0"</span>,
    <span class="hljs-attr">"lodash"</span>: <span class="hljs-string">"^4.17.21"</span>,
    <span class="hljs-attr">"@my-workspace/ui-legacy"</span>: <span class="hljs-string">"*"</span>,
    <span class="hljs-attr">"@my-workspace/shared-utils"</span>: <span class="hljs-string">"*"</span>
  }
}
</code></pre>
<p><strong>Admin Portal</strong> <code>package.json</code> (Different UI library):</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"@my-workspace/admin-portal"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.5.2"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^19.2.0"</span>,
    <span class="hljs-attr">"@mui/material"</span>: <span class="hljs-string">"^6.0.0"</span>,
    <span class="hljs-attr">"@emotion/react"</span>: <span class="hljs-string">"^11.11.0"</span>,
    <span class="hljs-attr">"recharts"</span>: <span class="hljs-string">"^2.10.0"</span>,
    <span class="hljs-attr">"@my-workspace/shared-utils"</span>: <span class="hljs-string">"*"</span>
  }
}
</code></pre>
<h3 id="heading-key-structure-highlights">Key Structure Highlights</h3>
<p><strong>Apps Directory</strong>: Contains deployable applications. Each app has its own configuration and can be built independently.</p>
<p><strong>Libs/Packages Directory</strong>: Contains reusable code. In integrated mode, organized by scope (e.g., <code>shared</code>). In package-based mode, each package is independent.</p>
<p><strong>Project Configuration</strong>: Each project has a <code>project.json</code> file defining its build, test, lint, and serve targets.</p>
<p><strong>Path Mappings</strong>: The <code>tsconfig.base.json</code> creates clean imports:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Instead of: import { Button } from '../../../libs/shared/ui-components/src/lib/button/button'</span>
<span class="hljs-comment">// You write:</span>
<span class="hljs-keyword">import</span> { Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'@my-workspace/ui-components'</span>;

<span class="hljs-comment">// Package-based with version-specific libraries:</span>
<span class="hljs-keyword">import</span> { Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'@my-workspace/ui-modern'</span>;  <span class="hljs-comment">// For React 19 apps</span>
<span class="hljs-keyword">import</span> { Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'@my-workspace/ui-legacy'</span>;  <span class="hljs-comment">// For React 18 apps</span>
</code></pre>
<p><strong>Dependency Graph</strong>: NX tracks dependencies between projects. For example:</p>
<ul>
<li><p><strong>Integrated</strong>: <code>customer-portal</code> → <code>ui-components</code>, <code>state</code>, <code>utils</code></p>
</li>
<li><p><strong>Package-Based</strong>:</p>
<ul>
<li><p><code>modern-app</code> (React 19) → <code>ui-modern</code>, <code>shared-utils</code></p>
</li>
<li><p><code>legacy-app</code> (React 18) → <code>ui-legacy</code>, <code>shared-utils</code></p>
</li>
<li><p><code>admin-portal</code> (React 19) → <code>shared-utils</code> (uses MUI instead of custom UI)</p>
</li>
</ul>
</li>
</ul>
<p>This structure scales from small teams to large enterprises, keeping your codebase organized, maintainable, and performant while supporting multiple React versions and varying dependency requirements.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>NX has matured into a comprehensive monorepo solution that dramatically improves developer productivity. With intelligent caching, affected-only commands, and modern tooling integration, it's an excellent choice for teams managing multiple related projects. Start small with a single app and library, then scale as your needs grow.</p>
<p>For more information, visit the <a target="_blank" href="https://nx.dev">official NX documentation</a>.</p>
]]></content:encoded></item><item><title><![CDATA[React Memorization]]></title><description><![CDATA[In React, when a parent component is updated, all of its child components will be rerendered even if the props are the same. It’s generally fine for simple components, but for those that perform expensive operations, there’s a performance gain in ens...]]></description><link>https://uidev.net/react-memorization</link><guid isPermaLink="true">https://uidev.net/react-memorization</guid><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Memoization]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Wed, 15 Oct 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/yGQmjh2uOTg/upload/42c1d07fc25ceb3b32ab5ce3ea9d622a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In React, when a parent component is updated, all of its child components will be rerendered even if the props are the same. It’s generally fine for simple components, but for those that perform expensive operations, there’s a performance gain in ensuring they are not rerendered unnecessarily using React memorization.</p>
<p>If you are using React v19, React automatically adds memoization when it’s necessary with the <a target="_blank" href="https://react.dev/learn/react-compiler">React Compiler</a>. React Compiler automatically analyzes code and memoizes components and functions that it determines will never change. It can automatically optimize performance by identifying and memoizing stable parts of the application at build time. If you are working on an older codebase, you would want to understand its usage.</p>
<h3 id="heading-reactmemo">React.Memo</h3>
<p>Just wrap the expensive function with it, and it will only rerender if the props change.</p>
<p>However, for objects and arrays, <code>React.memo</code> performs a shallow comparison of props. This means it only checks if the new object has the same reference in memory as the old object, not whether the contents of the objects are identical.</p>
<p>If the parent component creates a new object on every render, even if the new object contains the same values, the memoized child component will still rerender. So in this case, put in the second parameter to actually compare its value.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { memo, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> MemoizedChild = memo(<span class="hljs-function">(<span class="hljs-params">{ user }</span>) =&gt;</span> {
  <span class="hljs-comment">// expensive operations...</span>
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}, <span class="hljs-function">(<span class="hljs-params">prevProps, nextProps</span>) =&gt;</span> {
  <span class="hljs-comment">// actually compare its value</span>
  <span class="hljs-keyword">return</span> prevProps.user.name === nextProps.user.name;
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Parent</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

  <span class="hljs-comment">// A new user object is created every time Parent re-renders</span>
  <span class="hljs-keyword">const</span> user = { <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span> }; 

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount(c =&gt; c + 1)}&gt;Increment<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MemoizedChild</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-reactusememo-and-reactusecallback">React.useMemo and React.useCallback</h3>
<p>They both compare the value of the objects listed in the dependency list. useCallback is a wrapper of useMemo, so they do the same thing, and the syntax is very similar.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> render = useMemo(<span class="hljs-function">() =&gt;</span> <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> expensiveCall(user), [user]);
<span class="hljs-keyword">const</span> render2 = useCallback(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> expensiveCall(user), [user]);
</code></pre>
<p>The idea of memorization sounds very good, but don’t overuse it. Strings and numbers should never be memoized because they are compared by value, not by pointer. Also, there is a cost to make deep comparisons at every render, and it may cause bugs by not updating the child components correctly.</p>
]]></content:encoded></item><item><title><![CDATA[State Management Comparison: React Context vs MobX vs Redux]]></title><description><![CDATA[React Context
Overview
React Context is a built-in API for lightweight global state sharing. It’s ideal for passing data like themes, authentication, and language preferences down the component tree without manual prop drilling.
Best Use Cases

Stati...]]></description><link>https://uidev.net/state-management-comparison-react-context-vs-mobx-vs-redux</link><guid isPermaLink="true">https://uidev.net/state-management-comparison-react-context-vs-mobx-vs-redux</guid><category><![CDATA[React]]></category><category><![CDATA[Redux]]></category><category><![CDATA[MobX]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Wed, 15 Oct 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761354605428/05afddb2-5d57-4c81-b796-a9811b9e5828.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-react-context">React Context</h2>
<h3 id="heading-overview">Overview</h3>
<p>React Context is a built-in API for lightweight global state sharing. It’s ideal for passing data like themes, authentication, and language preferences down the component tree without manual prop drilling.</p>
<h3 id="heading-best-use-cases">Best Use Cases</h3>
<ol>
<li><p><strong>Static configuration or environment data</strong></p>
<ul>
<li>Themes (light/dark), app locale, and layout direction.</li>
</ul>
</li>
<li><p><strong>User authentication data</strong></p>
<ul>
<li>User objects, session tokens, or login states that only update occasionally.</li>
</ul>
</li>
<li><p><strong>Low-frequency UI preferences</strong></p>
<ul>
<li>Sidebar visibility, global UI toggles, or user settings shared across the app.</li>
</ul>
</li>
<li><p><strong>Dependency injection</strong></p>
<ul>
<li>Providing singletons or services (logger, analytics) across the app without prop chaining.</li>
</ul>
</li>
</ol>
<h3 id="heading-when-not-to-use">When Not to Use</h3>
<p>Avoid using Context for rapidly changing or complex state (e.g., real-time data, form state) since <strong>every consuming component re-renders</strong> when any part of the context value updates.​</p>
<h3 id="heading-example-projects">Example Projects</h3>
<ul>
<li><p>Static websites or documentation portals.</p>
</li>
<li><p>Small dashboards with limited global state.</p>
</li>
<li><p>Apps where global data rarely changes.</p>
</li>
</ul>
<hr />
<h2 id="heading-mobx">MobX</h2>
<h3 id="heading-overview-1">Overview</h3>
<p>MobX is a reactive state management library that uses observable data and computed values. You directly mutate observables, MobX tracks dependencies, and components re-render automatically. It shines when you need fine-grained reactivity and minimal boilerplate for updating complex, interdependent state values.</p>
<h3 id="heading-best-use-cases-1">Best Use Cases</h3>
<ol>
<li><p><strong>Medium-sized projects emphasizing reactivity</strong></p>
<ul>
<li>Live dashboards, trading terminals, or monitoring systems with auto-updating data streams.</li>
</ul>
</li>
<li><p><strong>Applications with real-time updates</strong></p>
<ul>
<li>Messaging apps, tracking interfaces, or collaborative documents.</li>
</ul>
</li>
<li><p><strong>Object-oriented state models</strong></p>
<ul>
<li>Domain stores like <code>UserStore</code>, <code>CartStore</code>, and <code>SettingsStore</code> reflecting real-world entities.</li>
</ul>
</li>
<li><p><strong>Interactive UI logic</strong></p>
<ul>
<li>Components with many derived values, filters, and computed relationships.</li>
</ul>
</li>
</ol>
<h3 id="heading-key-advantages">Key Advantages</h3>
<ul>
<li><p>Automatic and selective re-rendering based on observed data access.</p>
</li>
<li><p>Minimal ceremony — mutate state directly through actions.</p>
</li>
<li><p>Natural developer workflow for complex reactive UIs.</p>
</li>
</ul>
<h3 id="heading-when-not-to-use-1">When Not to Use</h3>
<ul>
<li><p>Large multi-team apps need a rigid structure or auditability.</p>
</li>
<li><p>Teams that rely heavily on explicit time-travel debugging and schema validation.​</p>
</li>
</ul>
<h3 id="heading-example-projects-1">Example Projects</h3>
<ul>
<li><p>Live analytics dashboards.</p>
</li>
<li><p>IoT or data visualization panels.</p>
</li>
<li><p>Real-time editors or chat tools.</p>
</li>
</ul>
<hr />
<h2 id="heading-redux">Redux</h2>
<h3 id="heading-overview-2">Overview</h3>
<p>Redux is a predictable, centralized state container built around immutability and a unidirectional data flow. You dispatch actions, reducers create new state, and components re-render. It’s designed for scalability, debugging clarity, and long-term maintainability in complex applications.</p>
<h3 id="heading-best-use-cases-2">Best Use Cases</h3>
<ol>
<li><p><strong>Large-scale applications</strong></p>
<ul>
<li>E-commerce platforms, analytics suites, and project management tools where consistency across views is essential.</li>
</ul>
</li>
<li><p><strong>Collaborative multi-developer teams</strong></p>
<ul>
<li>Its strict structure (actions, reducers, store) provides clear collaboration boundaries and conflict-free state management.</li>
</ul>
</li>
<li><p><strong>Apps requiring auditability</strong></p>
<ul>
<li>Systems that need time-travel debugging, rollback, or user-session replays.</li>
</ul>
</li>
<li><p><strong>Complex asynchronous flows</strong></p>
<ul>
<li>APIs managed with Redux Thunk, Redux Saga, or RTK Query.</li>
</ul>
</li>
</ol>
<h3 id="heading-key-advantages-1">Key Advantages</h3>
<ul>
<li><p>Predictability through pure reducers and explicit state transitions.</p>
</li>
<li><p>Advanced dev tooling and ecosystem maturity.</p>
</li>
<li><p>Easy integration with testing frameworks.</p>
</li>
</ul>
<h3 id="heading-when-not-to-use-2">When Not to Use</h3>
<ul>
<li><p>Smaller apps or prototypes where overhead from reducers and action patterns outweighs benefits.</p>
</li>
<li><p>UIs focused on frequent, granular state changes — MobX is more performant in those cases.​</p>
</li>
</ul>
<h3 id="heading-example-projects-2">Example Projects</h3>
<ul>
<li><p>Large enterprise or SaaS products with distributed teams.</p>
</li>
<li><p>Financial dashboards or product catalogs with multiple layers of derived data.</p>
</li>
<li><p>Any application whose global state must remain auditable across sessions.</p>
</li>
</ul>
<hr />
<h2 id="heading-detailed-comparison">Detailed Comparison</h2>
<p>Here is a detailed comparison of React Context, MobX, and Redux, highlighting their differences in philosophy, implementation, and performance across key dimensions.​</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Category</strong></td><td><strong>React Context</strong></td><td><strong>MobX</strong></td><td><strong>Redux</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Core Purpose</strong></td><td>Share data across components without prop drilling; not a full state management library</td><td>Reactive state management using observables and automatic tracking</td><td>Predictable global state container with strict unidirectional data flow</td></tr>
<tr>
<td><strong>Architecture Paradigm</strong></td><td>Context Provider &amp; Consumer</td><td>Reactive programming, object-oriented style</td><td>Functional programming with pure reducers</td></tr>
<tr>
<td><strong>State Model</strong></td><td>Plain mutable React state stored in Context value</td><td>Observable, mutable state with automatic dependency tracking</td><td>Immutable state replaced via pure reducers</td></tr>
<tr>
<td><strong>Data Flow</strong></td><td>Downward, through Provider value updates</td><td>Two-way observation with automatic reactions</td><td>Unidirectional flow from dispatch → reducer → new state</td></tr>
<tr>
<td><strong>Re-render Behavior</strong></td><td>All consumers re-render when context value changes</td><td>Only components observing changed observables re-render</td><td>Controlled via store subscription and <code>connect()</code>/<code>useSelector()</code></td></tr>
<tr>
<td><strong>Granularity of Updates</strong></td><td>Coarse (entire context consumers re-render)</td><td>Fine-grained (field-level dependency tracking)</td><td>Medium (subscription-level granularity)</td></tr>
<tr>
<td><strong>Boilerplate Complexity</strong></td><td>Minimal, built-in hooks and providers</td><td>Low, uses decorators or <code>makeObservable</code> APIs</td><td>High, requires actions, reducers, and dispatch logic</td></tr>
<tr>
<td><strong>Debugging Tools</strong></td><td>Limited — React DevTools only</td><td>Basic logging or MobX DevTools</td><td>Advanced — Redux DevTools with time travel and history</td></tr>
<tr>
<td><strong>Middleware &amp; Side Effects</strong></td><td>Handled manually or with hooks</td><td>Through reactions and actions</td><td>Via middleware like Redux Thunk or Redux Saga</td></tr>
<tr>
<td><strong>Learning Curve</strong></td><td>Very low; part of React API</td><td>Low to moderate, especially for OOP developers</td><td>Moderate to high; conceptual and verbose</td></tr>
<tr>
<td><strong>Performance Characteristics</strong></td><td>Inefficient on frequent updates (global re-renders)</td><td>Highly efficient, minimal re-renders via observables</td><td>Efficient but requires memoization and selectors</td></tr>
<tr>
<td><strong>Scalability</strong></td><td>Best for small/global configuration data</td><td>Best for small to medium apps emphasizing reactivity</td><td>Best for large, complex applications</td></tr>
<tr>
<td><strong>Type of Stores</strong></td><td>Usually single context per concern</td><td>Multiple stores per domain or feature</td><td>Single global store</td></tr>
<tr>
<td><strong>Side Effect Management</strong></td><td>Custom hooks or effects</td><td>Managed in reactions or actions</td><td>Via middleware (Saga, Thunk, etc.)</td></tr>
<tr>
<td><strong>Debuggability and Predictability</strong></td><td>Uncontrolled updates, can be opaque</td><td>Implicit reactivity can hide flow</td><td>Deterministic and traceable</td></tr>
<tr>
<td><strong>Best Use Cases</strong></td><td>Theming, authentication, user settings</td><td>Dashboards, reactive UIs, small-to-medium projects</td><td>Enterprise-scale apps, complex data flow</td></tr>
<tr>
<td><strong>Community &amp; Ecosystem</strong></td><td>Core React API, built-in</td><td>Active, smaller ecosystem</td><td>Very large ecosystem, mature tooling</td></tr>
<tr>
<td><strong>Mutability</strong></td><td>Mutable via React state</td><td>Mutable observables</td><td>Immutable reducers</td></tr>
<tr>
<td><strong>Development Style</strong></td><td>Declarative with manual subscription</td><td>Declarative with automatic observation</td><td>Declarative but manual wiring of actions</td></tr>
<tr>
<td><strong>Typical Component Integration</strong></td><td><code>useContext()</code> hook</td><td><code>observer()</code> wrapped components</td><td><code>connect()</code> HOC or <code>useSelector()</code> hook</td></tr>
<tr>
<td><strong>Error Handling &amp; Debugging Transparency</strong></td><td>Limited insight into triggers</td><td>Harder to trace implicit reactions</td><td>Extremely transparent via explicit actions and DevTools</td></tr>
</tbody>
</table>
</div><h2 id="heading-summary-interpretation">Summary Interpretation</h2>
<ul>
<li><p><strong>React Context</strong>: Lightweight and built-in—ideal for themes, configuration, or simple shared state.</p>
</li>
<li><p><strong>MobX</strong>: Prioritizes developer ergonomics and render performance through automatic reactivity; excellent for medium-sized, UI-focused apps.</p>
</li>
<li><p><strong>Redux</strong>: Best for large, collaborative, or enterprise apps needing explicitness, scalability, and auditability for the global state.</p>
</li>
</ul>
<p>Choosing between them depends on whether your top priority is ease, reactivity, or control.​ MobX delivers the most efficient re-render performance, Redux offers the most predictability and debugging power, and Context remains the simplest global state-sharing option when reactivity isn’t essential.</p>
]]></content:encoded></item><item><title><![CDATA[How to Keep Track of Counts in an Array]]></title><description><![CDATA[Here is the instruction for the coding exercise:
Create a function that takes in an array of numbers and returns an object in the specified format based on their counts.
Input array: [1, 2, 3, 3, 4, 3, 2, 4, 4, 5, 4]

Expected return: 
{
  unique: [1...]]></description><link>https://uidev.net/how-to-keep-track-of-counts-in-an-array</link><guid isPermaLink="true">https://uidev.net/how-to-keep-track-of-counts-in-an-array</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[data structures]]></category><category><![CDATA[#howtos]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Sat, 11 Oct 2025 14:00:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/gdL-UZfnD3I/upload/cbe53bd4f19e488a9279995037b56247.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here is the instruction for the coding exercise:</p>
<p>Create a function that takes in an array of numbers and returns an object in the specified format based on their counts.</p>
<pre><code class="lang-typescript">Input array: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>]

Expected <span class="hljs-keyword">return</span>: 
{
  unique: [<span class="hljs-number">1</span>, <span class="hljs-number">5</span>],
  twice: [<span class="hljs-number">2</span>],
  threePlus: [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>]
}
</code></pre>
<p>Notice the keys in the return object are set. So a type should be created for it. What are the other questions you could ask?</p>
<ol>
<li><p>Should there be an empty array in the return object if there is no value in that category, or not have the category in the object at all?</p>
</li>
<li><p>Are the numbers sorted?</p>
</li>
</ol>
<p>Below is a solution to set an empty array by default and keep the order of the numbers in the original list:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>];

<span class="hljs-keyword">type</span> CountType = <span class="hljs-string">'unique'</span> | <span class="hljs-string">'twice'</span> | <span class="hljs-string">'threePlus'</span>;

<span class="hljs-keyword">let</span> counts: Record&lt;CountType, <span class="hljs-built_in">number</span>[]&gt; = {
  unique: [],
  twice: [],
  threePlus: []
};
<span class="hljs-keyword">let</span> countMap: Record&lt;<span class="hljs-built_in">number</span>, <span class="hljs-built_in">number</span>&gt; = {};

<span class="hljs-keyword">const</span> checkCount = (numbers: <span class="hljs-built_in">number</span>[]): Record&lt;CountType, <span class="hljs-built_in">number</span>[]&gt; =&gt; {

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> n <span class="hljs-keyword">of</span> numbers) {
    countMap[n] = countMap[n] ? countMap[n]+<span class="hljs-number">1</span> : <span class="hljs-number">1</span>
  }
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> c <span class="hljs-keyword">in</span> countMap){
    <span class="hljs-keyword">if</span>(countMap[c] === <span class="hljs-number">1</span>){
      counts.unique.push(<span class="hljs-built_in">Number</span>(c)); <span class="hljs-comment">//when you set a number as the key, js converts it into string</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (countMap[c] === <span class="hljs-number">2</span>){
      counts.twice.push(<span class="hljs-built_in">Number</span>(c));
    } <span class="hljs-keyword">else</span> {
      counts.threePlus.push(<span class="hljs-built_in">Number</span>(c));
    }
  }
  <span class="hljs-keyword">return</span> counts;
}
</code></pre>
<p>If the numbers in the returned object should be sorted, the list should be sorted at the beginning instead of looping through the values in the return object again. And you won’t need the map to keep track of the count because you can compare the previous number with the next and get the count of that number.</p>
]]></content:encoded></item><item><title><![CDATA[How to Remove Duplicates from a String]]></title><description><![CDATA[You are given this coding instruction:
Create a function to remove duplicates and empty spaces in a given string.
Step one is always to consider all possible conditions and ask questions to clarify. For example:

Should the check be case sensitive?

...]]></description><link>https://uidev.net/how-to-remove-duplicates-from-a-string</link><guid isPermaLink="true">https://uidev.net/how-to-remove-duplicates-from-a-string</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[#howtos]]></category><category><![CDATA[interview-prep]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Fri, 10 Oct 2025 14:00:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/TNO9gxHhhj0/upload/45a081367a876b3e0fe4f478bf533633.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You are given this coding instruction:</p>
<p>Create a function to remove duplicates and empty spaces in a given string.</p>
<p>Step one is always to consider all possible conditions and ask questions to clarify. For example:</p>
<ol>
<li><p>Should the check be case sensitive?</p>
</li>
<li><p>Does the new string keep the same casing and order?</p>
</li>
</ol>
<p>Next, consider what data structures should be used with the conditions to meet the requirements.</p>
<p>In general, coding exercises involving strings are easier to handle by converting them into an array because it has a lot of <a target="_blank" href="https://uidev.net/javascript-array-methods-that-you-need-to-know">handy methods</a>. But it all comes down to the time and space complexity. For simple changes, even converting into a different data structure itself is an overhead cost. Another consideration is whether you can use an object for its advantage of O(1).</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> removeDuplicates = (str:<span class="hljs-built_in">string</span>): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span>{

  <span class="hljs-keyword">type</span> letterMapType = {[key:<span class="hljs-built_in">string</span>]: <span class="hljs-built_in">number</span>};
  <span class="hljs-keyword">let</span> letterMap:letterMapType ={};
  <span class="hljs-keyword">let</span> uniqueString = <span class="hljs-string">""</span>;

  <span class="hljs-keyword">for</span>(<span class="hljs-keyword">const</span> letter <span class="hljs-keyword">of</span> str){
    <span class="hljs-keyword">const</span> letterLowerCase = letter.toLowerCase();
    <span class="hljs-keyword">if</span>(letter === <span class="hljs-string">" "</span>){
        <span class="hljs-keyword">continue</span>;
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(!letterMap[letterLowerCase]){
        uniqueString += letter;
        letterMap[letterLowerCase] = <span class="hljs-number">1</span>;
    }
  }
  <span class="hljs-keyword">return</span> uniqueString;
}

removeDuplicates(<span class="hljs-string">'This is a test. TEST1 Test2 Done!'</span>); <span class="hljs-comment">// "Thisae.12Don!"</span>
</code></pre>
<p>Also, for the questions involving uniqueness, consider using Set. One thing to remember is that Sets are case-sensitive, so an array needs to be converted to lowercase first, then a Set for a case-insensitive check. The output will then be all in lowercase as well, unless we loop through the original string again.</p>
<p>So let’s say the requirement for the function is simply to remove any duplicate letters in a case-sensitive way. Below is a quick solution using Set.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> removeDuplicates = (str:<span class="hljs-built_in">string</span>): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span>{

  <span class="hljs-keyword">const</span> letterSet = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>(str.split(<span class="hljs-string">''</span>));
  <span class="hljs-keyword">return</span> [...letterSet].join(<span class="hljs-string">''</span>);

}

removeDuplicates(<span class="hljs-string">'This is a test. TEST1 Test2 Done!'</span>); <span class="hljs-comment">//"This ate.ES12Don!"</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[JavaScript Array Methods That You Need To Know]]></title><description><![CDATA[Array.from()
 Convert an iterable into an array. Can also be done with the spread operator […iterable].

Array.isArray()
 Check if a value is an array.

.includes()
 Check if the array contains a given value.
 [1, 2, 7, -1, 4].includes(-1) // true


...]]></description><link>https://uidev.net/javascript-array-methods-that-you-need-to-know</link><guid isPermaLink="true">https://uidev.net/javascript-array-methods-that-you-need-to-know</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[data structures]]></category><category><![CDATA[array methods]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Thu, 09 Oct 2025 23:38:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/c8Ig9Xka5iM/upload/00f14028f2c70bb9c1fc8b2eea363497.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<ol>
<li><p><strong>Array.from()</strong></p>
<p> Convert an iterable into an array. Can also be done with the spread operator […iterable].</p>
</li>
<li><p><strong>Array.isArray()</strong></p>
<p> Check if a value is an array.</p>
</li>
<li><p><strong>.includes()</strong></p>
<p> Check if the array contains a given value.</p>
<pre><code class="lang-javascript"> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">4</span>].includes(<span class="hljs-number">-1</span>) <span class="hljs-comment">// true</span>
</code></pre>
</li>
<li><p><strong>.forEach()</strong> &amp; <strong>.map()</strong></p>
<p> They both loop through an array. .forEach() is mainly for iterating over the loop. .map() returns a new array with the transformation.</p>
</li>
<li><p><strong>.filter()</strong></p>
<p> Return an array that is truthy with the given condition.</p>
<pre><code class="lang-javascript"> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">4</span>].filter(<span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> value &lt; <span class="hljs-number">4</span>); <span class="hljs-comment">// [1, 2, 3, -1]</span>
</code></pre>
</li>
<li><p><strong>.reduce()</strong></p>
<p> Probably the most versatile method of Array. It allows you to loop through an array, do any operations, and return anything you like.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> fruits = [<span class="hljs-string">"apple"</span>, <span class="hljs-string">"banana"</span>, <span class="hljs-string">"apple"</span>, <span class="hljs-string">"orange"</span>, <span class="hljs-string">"banana"</span>, <span class="hljs-string">"apple"</span>];
 <span class="hljs-keyword">const</span> fruitCounts = fruits.reduce(<span class="hljs-function">(<span class="hljs-params">accumulator, fruit</span>) =&gt;</span> {
   accumulator[fruit] = (accumulator[fruit] || <span class="hljs-number">0</span>) + <span class="hljs-number">1</span>;
   <span class="hljs-keyword">return</span> accumulator;
 }, {}); <span class="hljs-comment">// fruitCounts is { apple: 3, banana: 2, orange: 1 }</span>
</code></pre>
</li>
<li><p><strong>.join()</strong></p>
<p> Convert an array into a string.</p>
</li>
<li><p><strong>.concat()</strong></p>
<p> Return a new array with additional items. The difference with .push is that .concat doesn’t mutate the original array.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
 <span class="hljs-keyword">const</span> newArray = array.concat(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>); <span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
 <span class="hljs-keyword">const</span> anotherArray = array.concat([<span class="hljs-number">4</span>, <span class="hljs-number">5</span>], [<span class="hljs-number">6</span>]); <span class="hljs-comment">// [1, 2, 3, 4, 5, 6]</span>
 <span class="hljs-keyword">const</span> nestedArray = array.concat([[<span class="hljs-number">4</span>, <span class="hljs-number">5</span>]], [<span class="hljs-number">6</span>]); <span class="hljs-comment">// [1, 2, 3, [4, 5], 6]</span>
</code></pre>
</li>
<li><p><strong>.slice()</strong></p>
<p> Retrieve part of the array using the optional arguments - .slice(startIndex, endIndex). startIndex is default to 0, and endIndex is default to the length of the array.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>]
 <span class="hljs-keyword">const</span> array1 = array.slice() <span class="hljs-comment">// shallow copy of array [1, 2, 3, 4, 5, 6, 7]</span>
 <span class="hljs-keyword">const</span> array2 = array.slice(<span class="hljs-number">1</span>, array.length - <span class="hljs-number">1</span>) <span class="hljs-comment">// [2, 3, 4, 5, 6]</span>
 <span class="hljs-keyword">const</span> array3 = array.slice(<span class="hljs-number">-2</span>) <span class="hljs-comment">// last two values</span>
 <span class="hljs-keyword">const</span> array4 = array.slice(<span class="hljs-number">-10</span>) <span class="hljs-comment">// greater than the length, same as .slice()</span>
</code></pre>
</li>
<li><p><strong>.reverse()</strong></p>
<p>Change an array to be in reverse order.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
array.reverse(); <span class="hljs-comment">// array is now [4, 3, 2, 1]</span>
</code></pre>
</li>
<li><p>.<strong>flat()</strong></p>
<p>Flatten a nested array. The default flatten level is 1.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> nestedArray = [<span class="hljs-number">1</span>, [[[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>], <span class="hljs-number">4</span>], <span class="hljs-number">5</span>]];
<span class="hljs-keyword">const</span> flattenedNestedArray = nestedArray.flat(); <span class="hljs-comment">// [1, [[[2, 3], 4], 5]]</span>
<span class="hljs-keyword">const</span> flattenedNestedArrayL3 = nestedArray.flat(<span class="hljs-number">3</span>); <span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
</code></pre>
</li>
<li><p><strong>.push()</strong> &amp; <strong>.pop()</strong></p>
<p>.push() - add items to the end of the array.</p>
<p>.pop() - remove the last item from the array.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
array.push(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>); <span class="hljs-comment">// array is now [1, 2, 3, 4, 5]</span>
array.pop(); <span class="hljs-comment">// array is now [1, 2, 3, 4]</span>
</code></pre>
</li>
<li><p><strong>.shift()</strong> &amp; .<strong>unshift()</strong></p>
<p>.unshift() - add items to the beginning of the array.</p>
<p>.shift() - remove the first item from the array.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
array.unshift(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>); <span class="hljs-comment">// array is now [0, 1, 2, 3, 4]</span>
array.shift(); <span class="hljs-comment">// array is now [1, 2, 3, 4]</span>
</code></pre>
</li>
<li><p><strong>.sort()</strong></p>
<p>Sort an array alphabetically by default. Note that it modifies the original array. So the best practice is to make a copy using .slice() or the spread operator.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> users = [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Alice'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'alice@example.com'</span>, <span class="hljs-attr">isActive</span>: <span class="hljs-literal">true</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Bob'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'bob@example.com'</span>, <span class="hljs-attr">isActive</span>: <span class="hljs-literal">false</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Charlie'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'charlie@example.com'</span>, <span class="hljs-attr">isActive</span>: <span class="hljs-literal">true</span> },
];

<span class="hljs-keyword">const</span> activeUserEmailsSorted = users
  .filter(<span class="hljs-function"><span class="hljs-params">user</span> =&gt;</span> user.isActive) <span class="hljs-comment">// filter to keep the active users</span>
  .map(<span class="hljs-function"><span class="hljs-params">user</span> =&gt;</span> user.email.toUpperCase()) <span class="hljs-comment">// keep the emails in uppercase only</span>
  .sort(); <span class="hljs-comment">// sort the emails alphabetically</span>

<span class="hljs-comment">// activeUserEmailsSorted is ["ALICE@EXAMPLE.COM", "CHARLIE@EXAMPLE.COM"]</span>
</code></pre>
<p>To sort an array of numbers in ascending order</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> numbers = [<span class="hljs-number">40</span>, <span class="hljs-number">100</span>, <span class="hljs-number">1</span>, <span class="hljs-number">6</span>, <span class="hljs-number">25</span>, <span class="hljs-number">10</span>];
[...numbers].sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b); <span class="hljs-comment">// [1, 6, 10, 25, 40, 100]</span>
</code></pre>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Zod: TypeScript-first Schema Validation]]></title><description><![CDATA[Why is schema validation needed with TypeScript?
TypeScript has become the de facto way to write JavaScript applications because it sets type guards at build time to ensure the code logic is as intended throughout the codebase. But when the applicati...]]></description><link>https://uidev.net/zod-typescript-first-schema-validation</link><guid isPermaLink="true">https://uidev.net/zod-typescript-first-schema-validation</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Tue, 30 Sep 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Q-ioK6NPFos/upload/aefb30a208903d721c8f3d222e5e4f1a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-why-is-schema-validation-needed-with-typescript">Why is schema validation needed with TypeScript?</h3>
<p>TypeScript has become the de facto way to write JavaScript applications because it sets type guards at build time to ensure the code logic is as intended throughout the codebase. But when the application interacts with an external data source, it’s not something TypeScript can foresee and validate.</p>
<p>One of the most essential aspects of frontend development is collaborating with the backend developer to determine the necessary data in an optimal data structure. And there will be API changes later. How can we verify that the UI codebase and the backend are in sync? That is where <a target="_blank" href="https://zod.dev/">Zod</a> comes in — runtime validation.</p>
<h3 id="heading-zod-at-a-glance">Zod at a Glance</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> z <span class="hljs-keyword">from</span> <span class="hljs-string">"zod"</span>;

<span class="hljs-comment">// create a schema for a post</span>
<span class="hljs-keyword">const</span> PostSchema = z.object({
  <span class="hljs-attr">id</span>: z.coerce.number(), <span class="hljs-comment">//coerce string or boolean into number</span>
  <span class="hljs-attr">title</span>: z.string(),
  <span class="hljs-attr">body</span>: z.string().nullable(), <span class="hljs-comment">// string | null </span>
  <span class="hljs-attr">email</span>: z.email(),  <span class="hljs-comment">//email validation</span>
  <span class="hljs-attr">published</span>: z.coerce.boolean.(), 
  <span class="hljs-attr">tags</span>: z.array(z.string()).default([]) <span class="hljs-comment">//array of string and default to an empty array</span>
});

<span class="hljs-comment">// create a type based on the schema</span>
type Post = z.infer&lt;<span class="hljs-keyword">typeof</span> PostSchema&gt;;

<span class="hljs-keyword">const</span> getPose = <span class="hljs-keyword">async</span> (id: number) =&gt; {
  <span class="hljs-comment">// fetch mock data from jsonplaceholder</span>
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(https:<span class="hljs-comment">//jsonplaceholder.typicode.com/posts/${id});</span>
  <span class="hljs-keyword">const</span> post = response.json();

  <span class="hljs-comment">// the parsed result is validated and type safe</span>
  <span class="hljs-keyword">const</span> post = PostSchema parse(post);
  <span class="hljs-keyword">return</span> post;
}
</code></pre>
<p>If you have a type defined already, add validation to ensure the schema is validated against the type.</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">const</span> PostSchema = z.object({
  <span class="hljs-attr">id</span>: z.coerce.number(), 
  <span class="hljs-attr">title</span>: z.string(),
  <span class="hljs-attr">body</span>: z.string().nullable() 
}) satisfies z.ZodType&lt;Post&gt;;
</code></pre>
<p>If you are excited to use Zod in your application, check out their <a target="_blank" href="https://zod.dev/">website</a> for additional advanced features, including error handling, custom error messages, and metadata. There is also an excellent <a target="_blank" href="https://www.totaltypescript.com/tutorials/zod">tutorial</a> to get you started.</p>
]]></content:encoded></item><item><title><![CDATA[How to Flatten an Array]]></title><description><![CDATA[Here is the instruction for this coding exercise:
Create a function to flatten an array without using the Array method .flat()
Given an array [1,2,[3,4, [5,6,7], 8], 9, [10, [20], 30]]
The function should return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30...]]></description><link>https://uidev.net/how-to-flatten-an-array</link><guid isPermaLink="true">https://uidev.net/how-to-flatten-an-array</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[#howtos]]></category><category><![CDATA[coding challenge]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Mon, 15 Sep 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cckf4TsHAuw/upload/c0ce87740b81f1df22a2ac6ecbea99c2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here is the instruction for this coding exercise:</p>
<p>Create a function to flatten an array without using the Array method .flat()</p>
<p>Given an array [1,2,[3,4, [5,6,7], 8], 9, [10, [20], 30]]</p>
<p>The function should return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]</p>
<p>Note in the example array, the number of levels is unspecified, and it can be nested in any way.</p>
<p>Looping through a list and repeating the same operation with different parts should prompt you to use recursion. Below is a decent solution.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> exampleArray = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,[<span class="hljs-number">3</span>,<span class="hljs-number">4</span>, [<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>], <span class="hljs-number">8</span>], <span class="hljs-number">9</span>, [<span class="hljs-number">10</span>, [<span class="hljs-number">20</span>], <span class="hljs-number">30</span>]];
<span class="hljs-keyword">let</span> flattenArray: <span class="hljs-built_in">number</span>[] = [];
<span class="hljs-keyword">type</span> DeepArray&lt;T&gt; = <span class="hljs-built_in">Array</span>&lt;T | DeepArray&lt;T&gt;&gt;;

<span class="hljs-keyword">const</span> flatten = (numbers: DeepArray&lt;<span class="hljs-built_in">number</span>&gt;): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> num <span class="hljs-keyword">of</span> numbers) {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(num)){
      flatten(num);
    } <span class="hljs-keyword">else</span> {
      flattenArray.push(num);
    }
  }
}

flatten(exampleArray); <span class="hljs-comment">// [1,2,3,4,5,6,7,8,9,10]</span>
</code></pre>
<p>If you want something fancier, use the Array method reduce.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> exampleArray = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,[<span class="hljs-number">3</span>,<span class="hljs-number">4</span>, [<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>], <span class="hljs-number">8</span>], <span class="hljs-number">9</span>, [<span class="hljs-number">10</span>, [<span class="hljs-number">20</span>], <span class="hljs-number">30</span>]];

<span class="hljs-keyword">type</span> NestedNumberArray = <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span> | NestedNumberArray&gt;;

<span class="hljs-keyword">const</span> flatten = (numbers: NestedNumberArray): <span class="hljs-function"><span class="hljs-params">NestedNumberArray</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> numbers.reduce(<span class="hljs-function">(<span class="hljs-params">accu: NestedNumberArray, num: <span class="hljs-built_in">number</span> | NestedNumberArray</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(num)){
      accu = accu.concat(flatten(num));
    } <span class="hljs-keyword">else</span> {
      accu.push(num);
    }
    <span class="hljs-keyword">return</span> accu;
  },[]);
}

flatten(exampleArray); <span class="hljs-comment">// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Learn CSS Flexbox in 10 Minutes]]></title><description><![CDATA[Flexbox is a powerful module in CSS that provides an efficient way to arrange elements in a container — even when their size is unknown or dynamic. Unlike traditional layouts that rely heavily on floats and positioning, Flexbox focuses on distributin...]]></description><link>https://uidev.net/learn-css-flexbox-in-10-minutes</link><guid isPermaLink="true">https://uidev.net/learn-css-flexbox-in-10-minutes</guid><category><![CDATA[CSS]]></category><category><![CDATA[flexbox]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Responsive Web Design]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Mon, 01 Sep 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Jh6GHkhsWos/upload/f65f19e3a8d6b0ee7c8fb0510af039b3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Flexbox is a powerful module in CSS that provides an efficient way to arrange elements in a container — even when their size is unknown or dynamic. Unlike traditional layouts that rely heavily on floats and positioning, Flexbox focuses on distributing space along a single axis (either horizontal or vertical) while allowing flexible alignment and spacing.</p>
<h2 id="heading-1-the-magical-property-flex">1. The Magical Property Flex</h2>
<p>With Flexbox, you define a <strong>flex container</strong> and then control how its <strong>flex items</strong> behave inside it — including their size, order, and alignment.</p>
<p>With a parent container with class ‘container‘ and child items inside it. The magic starts with just setting the display to flex:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
}
</code></pre>
<h2 id="heading-2-flexbox-properties">2. Flexbox Properties</h2>
<p><strong>Flex container properties:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Property</strong></td><td><strong>Description</strong></td><td><strong>Example</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>display</strong></td><td>Defines a flex container (flexorinline-flex)</td><td>display: flex;</td></tr>
<tr>
<td><strong>flex-direction</strong></td><td>Defines the direction of the main axis</td><td>flex-direction: row-reverse;</td></tr>
<tr>
<td><strong>flex-wrap</strong></td><td>Allows items to wrap onto multiple lines</td><td>flex-wrap: wrap;</td></tr>
<tr>
<td><strong>flex-flow</strong></td><td>Shorthand for <strong>flex-direction</strong> and <strong>flex-wrap</strong></td><td>flex-flow: column wrap;</td></tr>
<tr>
<td><strong>justify-content</strong></td><td>Aligns items along the main axis</td><td>justify-content: space-between;</td></tr>
<tr>
<td><strong>align-items</strong></td><td>Aligns items along the cross-axis</td><td>align-items: center;</td></tr>
<tr>
<td><strong>align-content</strong></td><td>Aligns rows within the container when wrapping</td><td>align-content: space-around;</td></tr>
</tbody>
</table>
</div><p><strong>Flex item properties:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Property</strong></td><td><strong>Description</strong></td><td><strong>Example</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>order</strong></td><td>Controls the order of the items</td><td>order: 2;</td></tr>
<tr>
<td><strong>flex-grow</strong></td><td>Defines how much a flex item can grow relative to others</td><td>flex-grow: 1;</td></tr>
<tr>
<td><strong>flex-shrink</strong></td><td>Defines how much a flex item can shrink relative to others</td><td>flex-shrink: 0;</td></tr>
<tr>
<td><strong>flex-basis</strong></td><td>Defines the initial size of a flex item before distributing space</td><td>flex-basis: 200px;</td></tr>
<tr>
<td><strong>flex</strong></td><td>Shorthand for <strong>flex-grow, flex-shrink</strong>, and <strong>flex-basis</strong></td><td>flex: 1 0 100px;</td></tr>
<tr>
<td><strong>align-self</strong></td><td>Overrides <strong>align-items</strong> for an individual item</td><td>align-self: flex-end;</td></tr>
</tbody>
</table>
</div><h2 id="heading-3-example-of-a-responsive-two-column-layout">3. Example of a Responsive Two-Column Layout</h2>
<pre><code class="lang-css"><span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.sidebar</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
}
<span class="hljs-selector-class">.main</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">3</span>;
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Implement Debounce and Throttle in JavaScript]]></title><description><![CDATA[When building interactive web applications, performance often becomes a concern—especially when dealing with events that fire frequently, like scrolling, resizing, or key presses. Without control, these event handlers can overwhelm the browser and AP...]]></description><link>https://uidev.net/implement-debounce-and-throttle-in-javascript</link><guid isPermaLink="true">https://uidev.net/implement-debounce-and-throttle-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ratelimit]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Mon, 25 Aug 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/evcG0YvLFoY/upload/533d7137f732aee33ea038638498ab0b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When building interactive web applications, performance often becomes a concern—especially when dealing with events that fire frequently, like scrolling, resizing, or key presses. Without control, these event handlers can overwhelm the browser and APIs. This is where <strong>debounce</strong> and <strong>throttle</strong> come in: two powerful techniques to control how often a function executes.</p>
<h2 id="heading-why-use-debounce-and-throttle">Why Use Debounce and Throttle</h2>
<ul>
<li><p><strong>Debounce:</strong> Ensures a function only runs <em>after</em> a specified delay from the last time it was called. Ideal for search boxes, window resizing, or form validation, where you don't want to query the server on every keystroke—only after the user stops typing.</p>
</li>
<li><p><strong>Throttle:</strong> Ensures a function runs at most once in a set time interval. Perfect for scroll and resize events, where you want smooth updates without overwhelming the browser or APIs.</p>
</li>
</ul>
<h2 id="heading-debounce-implementation">Debounce Implementation</h2>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debounce</span>(<span class="hljs-params">func, delay</span>) </span>{
  <span class="hljs-keyword">let</span> timeoutId;
  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">...args</span>) </span>{
    <span class="hljs-built_in">clearTimeout</span>(timeoutId);
    timeoutId = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> func.apply(<span class="hljs-built_in">this</span>, args), delay);
  };
}

<span class="hljs-keyword">const</span> test = <span class="hljs-function">(<span class="hljs-params">text</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(text);
<span class="hljs-keyword">const</span> debounceTest = debounce(test, <span class="hljs-number">10000</span>);

debounceTest(<span class="hljs-string">"Hello, World!"</span>);
<span class="hljs-comment">// this call cancles the first timer</span>
<span class="hljs-comment">// "Hello, World! 2" is logged after 15 seconds</span>
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> debounceTest(<span class="hljs-string">"Hello, World! 2"</span>), <span class="hljs-number">5000</span>);
</code></pre>
<h2 id="heading-throttle-implementation">Throttle Implementation</h2>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throttle</span>(<span class="hljs-params">func, limit</span>) </span>{
  <span class="hljs-keyword">let</span> inThrottle;
  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">...args</span>) </span>{
    <span class="hljs-keyword">if</span> (!inThrottle) {
      func.apply(<span class="hljs-built_in">this</span>, args);
      inThrottle = <span class="hljs-literal">true</span>;
      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> (inThrottle = <span class="hljs-literal">false</span>), limit);
    }
  };
}

<span class="hljs-keyword">const</span> test = <span class="hljs-function">(<span class="hljs-params">text</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(text);
<span class="hljs-keyword">const</span> throttledTest = throttle(test, <span class="hljs-number">10000</span>);

<span class="hljs-comment">//only the first one is logged</span>
throttledTest(<span class="hljs-string">"Hello, World! One and only."</span>);
throttledTest(<span class="hljs-string">"Hello, World! 2"</span>);
throttledTest(<span class="hljs-string">"Hello, World! 3"</span>);

<span class="hljs-comment">// call again after the throttle time</span>
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">//the first one is logged</span>
  throttledTest(<span class="hljs-string">"Hello, World! Again"</span>);
  throttledTest(<span class="hljs-string">"Hello, World! 2"</span>);
}, <span class="hljs-number">15000</span>);
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Practical CSS Tips for Better UX and Accessibility]]></title><description><![CDATA[Focus & Interaction
1. Use :focus-visible for keyboard-only focus outlines
/* Outline is shown only for keyboard-only focus */
button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}

2. Never remove outlines without replacement
/*...]]></description><link>https://uidev.net/practical-css-tips-for-better-ux-and-accessibility</link><guid isPermaLink="true">https://uidev.net/practical-css-tips-for-better-ux-and-accessibility</guid><category><![CDATA[CSS]]></category><category><![CDATA[Accessibility]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[UX]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Sun, 10 Aug 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761592129860/346462be-48d6-496f-92f4-661d9d6b56fb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-focus-amp-interaction">Focus &amp; Interaction</h2>
<p><strong>1. Use</strong> <code>:focus-visible</code> for keyboard-only focus outlines</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Outline is shown only for keyboard-only focus */</span>
<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:focus-visible</span> {
  <span class="hljs-attribute">outline</span>: <span class="hljs-number">2px</span> solid blue;
  <span class="hljs-attribute">outline-offset</span>: <span class="hljs-number">2px</span>;
}
</code></pre>
<p><strong>2. Never remove outlines without replacement</strong></p>
<pre><code class="lang-css"><span class="hljs-comment">/* Bad */</span>
*<span class="hljs-selector-pseudo">:focus</span> { <span class="hljs-attribute">outline</span>: none; }

<span class="hljs-comment">/* Good */</span>
*<span class="hljs-selector-pseudo">:focus-visible</span> {
  <span class="hljs-attribute">outline</span>: <span class="hljs-number">2px</span> solid currentColor;
  <span class="hljs-attribute">outline-offset</span>: <span class="hljs-number">2px</span>;
}
</code></pre>
<p><strong>3. Use</strong> <code>prefers-reduced-motion</code> for animations</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-reduced-motion:</span> reduce) {
  *, *<span class="hljs-selector-pseudo">::before</span>, *<span class="hljs-selector-pseudo">::after</span> {
    <span class="hljs-attribute">animation-duration</span>: <span class="hljs-number">0.01ms</span> <span class="hljs-meta">!important</span>;
    <span class="hljs-attribute">transition-duration</span>: <span class="hljs-number">0.01ms</span> <span class="hljs-meta">!important</span>;
  }
}
</code></pre>
<h2 id="heading-color-amp-contrast">Color &amp; Contrast</h2>
<p><strong>4. Ensure sufficient color contrast</strong></p>
<ul>
<li><p>Text: minimum 4.5:1 ratio (WCAG AA)</p>
</li>
<li><p>Large text (18pt+): minimum 3:1 ratio</p>
</li>
<li><p>Use tools like WebAIM contrast checker</p>
</li>
</ul>
<p><strong>5. Don't rely on color alone</strong></p>
<p>Make sure there is text or other clear indications to convey the message.</p>
<p><strong>6. Use</strong> <code>currentColor</code> for flexible theming</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.icon</span> {
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid currentColor; <span class="hljs-comment">/* Inherits text color */</span>
}
</code></pre>
<h2 id="heading-typography">Typography</h2>
<p><strong>7. Use relative units for font sizes</strong></p>
<pre><code class="lang-css"><span class="hljs-comment">/* Respects user's browser font size preferences */</span>
<span class="hljs-selector-tag">body</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>; }
<span class="hljs-selector-tag">h1</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>; }
</code></pre>
<p><strong>8. Set comfortable line heights</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>; <span class="hljs-comment">/* WCAG recommends 1.5 minimum for body text */</span>
}
</code></pre>
<p><strong>9. Limit line length for readability</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">65ch</span>; <span class="hljs-comment">/* Optimal: 45-75 characters per line */</span>
}
</code></pre>
<h2 id="heading-responsive-design">Responsive Design</h2>
<p><strong>10. Use</strong> <code>clamp()</code> for fluid typography</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span> {
  <span class="hljs-comment">/* Minimum: 1.5rem (24px) - won't go smaller, even on tiny screens
     Preferred: 5vw (5% of viewport width) - scales with screen size
     Maximum: 3rem (48px) - won't go larger, even on huge screens
  */</span>
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.5rem</span>, <span class="hljs-number">5vw</span>, <span class="hljs-number">3rem</span>);
}
</code></pre>
<p><strong>11. Make tap targets large enough</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">button</span>, <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">44px</span>; <span class="hljs-comment">/* Apple &amp; Google recommend 44-48px */</span>
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">44px</span>;
}
</code></pre>
<p><strong>12. Use</strong> <code>gap</code> instead of margins in flex/grid</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">1rem</span>; <span class="hljs-comment">/* Cleaner than margin hacks */</span>
}
</code></pre>
<h2 id="heading-visibility-amp-display">Visibility &amp; Display</h2>
<p><strong>13. Add a custom class to hide content for screen readers only</strong></p>
<pre><code class="lang-css"><span class="hljs-comment">/* Screen reader accessible but visually hidden */</span>
<span class="hljs-selector-class">.sr-only</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">1px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">1px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin</span>: -<span class="hljs-number">1px</span>;
  <span class="hljs-attribute">overflow</span>: hidden;
  <span class="hljs-attribute">clip</span>: <span class="hljs-built_in">rect</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
  <span class="hljs-attribute">white-space</span>: nowrap;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">0</span>;
}
</code></pre>
<p><strong>14. Avoid</strong> <code>display: none</code> for focusable elements</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Bad - removes from tab order */</span>
<span class="hljs-selector-class">.hidden</span> { <span class="hljs-attribute">display</span>: none; }

<span class="hljs-comment">/* Good - visually hidden but still accessible */</span>
<span class="hljs-selector-class">.visually-hidden</span> { <span class="hljs-comment">/* use sr-only class above */</span> }
</code></pre>
<h2 id="heading-forms">Forms</h2>
<p><strong>15. Style form validation states clearly</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:invalid</span> {
  <span class="hljs-attribute">border-color</span>: red;
}
<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:valid</span> {
  <span class="hljs-attribute">border-color</span>: green;
}
<span class="hljs-comment">/* But only after interaction */</span>
<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:placeholder-shown)</span><span class="hljs-selector-pseudo">:invalid</span> {
  <span class="hljs-attribute">border-color</span>: red;
}
</code></pre>
<p><strong>16. Increase input padding for better touch targets</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span>, <span class="hljs-selector-tag">textarea</span>, <span class="hljs-selector-tag">select</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>; <span class="hljs-comment">/* Prevents zoom on iOS */</span>
}
</code></pre>
<p><strong>17. Style disabled states obviously</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:disabled</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.6</span>;
  <span class="hljs-attribute">cursor</span>: not-allowed;
}
</code></pre>
<h2 id="heading-layout-amp-spacing">Layout &amp; Spacing</h2>
<p><strong>18. Use logical properties for internationalization</strong></p>
<pre><code class="lang-css"><span class="hljs-comment">/* Instead of margin-left */</span>
<span class="hljs-selector-tag">margin-inline-start</span>: 1<span class="hljs-selector-tag">rem</span>; <span class="hljs-comment">/* Works for RTL languages */</span>
</code></pre>
<p><strong>19. Prefer</strong> <code>padding</code> over <code>height</code> for vertical spacing</p>
<pre><code class="lang-css"><span class="hljs-comment">/* More flexible and accessible */</span>
<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span> <span class="hljs-number">1.5rem</span>;
  <span class="hljs-comment">/* Not: height: 40px; */</span>
}
</code></pre>
<p><strong>20. Use</strong> <code>aspect-ratio</code> for responsive media</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.video-container</span> {
  <span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">16</span> / <span class="hljs-number">9</span>; <span class="hljs-comment">/* Modern browsers */</span>
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;

  <span class="hljs-comment">/* Fallback for older browsers */</span>
  @supports not (<span class="hljs-attribute">aspect-ratio</span>: <span class="hljs-number">16</span> / <span class="hljs-number">9</span>) {
    padding-bottom: <span class="hljs-number">56.25%</span>;
  }
}
</code></pre>
<h2 id="heading-dark-mode">Dark Mode</h2>
<p><strong>21. Support dark mode preferences</strong></p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme:</span> dark) {
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#1a1a1a</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#f0f0f0</span>;
  }
}
</code></pre>
<h2 id="heading-performance">Performance</h2>
<p><strong>22. Use</strong> <code>content-visibility</code> for long lists</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.list-item</span> {
  <span class="hljs-attribute">content-visibility</span>: auto;
  <span class="hljs-attribute">contain-intrinsic-size</span>: <span class="hljs-number">0</span> <span class="hljs-number">200px</span>;
}
</code></pre>
<p><strong>23. Optimize animations with</strong> <code>will-change</code></p>
<pre><code class="lang-css"><span class="hljs-selector-class">.animated</span> {
  <span class="hljs-attribute">will-change</span>: transform; <span class="hljs-comment">/* Use sparingly */</span>
}
</code></pre>
<h2 id="heading-misc">Misc</h2>
<p><strong>24. Use</strong> <code>pointer-events</code> carefully</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Be cautious - affects accessibility */</span>
<span class="hljs-selector-class">.overlay</span> {
  <span class="hljs-attribute">pointer-events</span>: none;
}
</code></pre>
<p>If you need to disable all user interactions on an element, consider using <code>inert</code> attribute:</p>
<ul>
<li><p>Removes elements from tab order</p>
</li>
<li><p>Prevents clicks</p>
</li>
<li><p>Hides from screen readers</p>
</li>
<li><p>Can be toggled with JavaScript</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// Toggle overlay</span>
overlay.inert = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Make interactive</span>
overlay.inert = <span class="hljs-literal">true</span>;  <span class="hljs-comment">// Make non-interactive</span>
</code></pre>
<p><strong>25. Add</strong> <code>:has()</code> for parent-based styling</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Style form if it has invalid input */</span>
<span class="hljs-selector-tag">form</span><span class="hljs-selector-pseudo">:has(input</span><span class="hljs-selector-pseudo">:invalid)</span> {
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid red;
}
</code></pre>
<p><strong>26. Use</strong> <code>scroll-margin-top</code> for anchor links</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:target</span> {
  <span class="hljs-attribute">scroll-margin-top</span>: <span class="hljs-number">80px</span>; <span class="hljs-comment">/* Accounts for fixed header */</span>
}
</code></pre>
<p><strong>27. Smooth scrolling (with motion preference)</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">scroll-behavior</span>: smooth;
}
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-reduced-motion:</span> reduce) {
  <span class="hljs-selector-tag">html</span> {
    <span class="hljs-attribute">scroll-behavior</span>: auto;
  }
}
</code></pre>
<p>These tips will help you build more accessible, user-friendly interfaces that work well for everyone, regardless of their device, abilities, or preferences.</p>
]]></content:encoded></item><item><title><![CDATA[Building a Documentation Website with Gatsby]]></title><description><![CDATA[When it comes to building modern documentation websites, Gatsby offers a powerful solution that combines the best of static site generation with the flexibility of React. In this guide, I'll walk you through setting up a documentation site using Gats...]]></description><link>https://uidev.net/building-a-documentation-website-with-gatsby</link><guid isPermaLink="true">https://uidev.net/building-a-documentation-website-with-gatsby</guid><category><![CDATA[Web Development]]></category><category><![CDATA[JAMstack]]></category><category><![CDATA[Gatsby]]></category><category><![CDATA[documentation]]></category><category><![CDATA[swagger]]></category><dc:creator><![CDATA[Ying]]></dc:creator><pubDate>Tue, 31 Oct 2023 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Oaqk7qqNh_c/upload/b5354fb27f0b9d5efdeed58792d2508e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When it comes to building modern documentation websites, Gatsby offers a powerful solution that combines the best of static site generation with the flexibility of React. In this guide, I'll walk you through setting up a documentation site using Gatsby, covering everything from the JAMstack architecture to handling complex API documentation with Swagger.</p>
<h2 id="heading-why-jamstack-and-gatsby">Why JAMstack and Gatsby?</h2>
<p>Before diving into the implementation, let's understand the foundation: <strong>JAMstack</strong> (JavaScript + APIs + Markup). This modern web development architecture is built on three core principles:</p>
<ul>
<li><p><strong>JavaScript</strong>: Client-side code that handles dynamic functionality</p>
</li>
<li><p><strong>APIs</strong>: External services accessed over HTTPS with JavaScript</p>
</li>
<li><p><strong>Markup</strong>: Static content including HTML, CSS, and images</p>
</li>
</ul>
<p>The key advantage? All static content is rendered at build time, not runtime. This approach delivers significant benefits:</p>
<ul>
<li><p><strong>Better performance</strong>: Static files can be hosted on CDNs, ensuring lightning-fast load times</p>
</li>
<li><p><strong>Enhanced security</strong>: No server-side processing means reduced attack surface</p>
</li>
<li><p><strong>SEO-friendly</strong>: Pre-rendered HTML is easily crawlable by search engines</p>
</li>
</ul>
<h2 id="heading-why-gatsby-for-documentation">Why Gatsby for Documentation?</h2>
<p>Gatsby excels at building documentation sites because it seamlessly handles multiple data sources. Whether you're working with Markdown files, WordPress, headless CMSs, or other sources, Gatsby's built-in GraphQL server fetches and processes data at build time, generating static files in the public folder.</p>
<p>For documentation sites, this means your Markdown files are converted into optimized HTML pages during the build process, resulting in a blazingly fast user experience.</p>
<h2 id="heading-setting-up-your-gatsby-documentation-site">Setting Up Your Gatsby Documentation Site</h2>
<h3 id="heading-initial-setup">Initial Setup</h3>
<p>Follow the <a target="_blank" href="https://www.gatsbyjs.com/docs/tutorial/getting-started/part-0/">Gatsby official tutorial</a> to set up your project</p>
<h3 id="heading-adding-sass-support">Adding Sass Support</h3>
<p>For styling flexibility, add Sass to your project:</p>
<pre><code class="lang-bash">npm install --save node-sass gatsby-plugin-sass
</code></pre>
<p>Configuration details can be found at <a target="_blank" href="https://www.gatsbyjs.com/plugins/">gatsby-plugin-sass</a>.</p>
<h3 id="heading-handling-local-markdown-files">Handling Local Markdown Files</h3>
<p>To work with local Markdown files, install the filesystem source plugin:</p>
<pre><code class="lang-bash">npm install --save gatsby-source-filesystem
</code></pre>
<p>This plugin allows Gatsby to read files from your local filesystem and make them available through GraphQL queries.</p>
<h3 id="heading-markdown-processing-and-syntax-highlighting">Markdown Processing and Syntax Highlighting</h3>
<p>Install the necessary packages for Markdown transformation and code syntax highlighting:</p>
<pre><code class="lang-bash">npm install --save gatsby-transformer-remark gatsby-remark-prismjs prismjs
</code></pre>
<p><strong>Prism.js</strong> provides beautiful syntax highlighting for code blocks in your documentation. You can preview and choose from various <a target="_blank" href="https://prismjs.com/">Prism themes</a> to match your branding.</p>
<p>If you need to embed React components directly in Markdown files, check out <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-remark-component/">gatsby-remark-component</a>.</p>
<h2 id="heading-working-with-gatsbys-data-layer">Working with Gatsby's Data Layer</h2>
<h3 id="heading-understanding-graphql-in-gatsby">Understanding GraphQL in Gatsby</h3>
<p>Gatsby comes with a built-in GraphQL server that makes data fetching elegant and powerful. You can explore your data using GraphiQL (typically at <code>http://localhost:8000/___graphql</code>).</p>
<p><strong>Pro tip</strong>: Press <code>Ctrl + Space</code> in GraphiQL to get autocomplete suggestions.</p>
<p>Here's a typical GraphQL query structure for Markdown content:</p>
<pre><code class="lang-graphql">{
  allMarkdownRemark {
    edges {
      node {
        html
        frontmatter {
          updated
        }
      }
    }
  }
}
</code></pre>
<p>The <code>edges</code> array contains <code>node</code> objects, where each node holds your actual content.</p>
<h3 id="heading-two-types-of-queries">Two Types of Queries</h3>
<p>Gatsby supports two distinct query patterns:</p>
<p><strong>Page Queries</strong>:</p>
<ul>
<li><p>Used in page components</p>
</li>
<li><p>Accept query variables via <code>pageContext</code></p>
</li>
<li><p>Perfect for dynamic page generation</p>
</li>
</ul>
<p><strong>Static Queries</strong>:</p>
<ul>
<li><p>Can be used in any component</p>
</li>
<li><p>Don't accept variables</p>
</li>
<li><p>Use the <code>useStaticQuery</code> hook or StaticQuery component</p>
</li>
</ul>
<h2 id="heading-building-dynamic-routes">Building Dynamic Routes</h2>
<h3 id="heading-creating-pages-with-gatsby-nodejs">Creating Pages with gatsby-node.js</h3>
<p>The <code>gatsby-node.js</code> file is where the magic happens for dynamic routing. Use <code>createNodeField()</code> to generate pages with friendly URLs based on your file structure.</p>
<p>Key steps:</p>
<ol>
<li><p>Create a page template component</p>
</li>
<li><p>Pass context data to the template</p>
</li>
<li><p>Use <code>dangerouslySetInnerHTML</code> to insert the generated HTML into your React component</p>
</li>
</ol>
<p>This approach allows you to create a documentation structure that mirrors your file organization, making it intuitive to manage.</p>
<h3 id="heading-navigation-with-gatsby-link">Navigation with Gatsby Link</h3>
<p>For internal navigation, always use Gatsby's <code>Link</code> component:</p>
<p><a target="_blank" href="https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-link/">https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-link/</a></p>
<p>This provides optimized routing with preloading for a snappy user experience.</p>
<h2 id="heading-styling-considerations">Styling Considerations</h2>
<p>While Gatsby comes with CSS Modules support, there are some gotchas to be aware of:</p>
<ul>
<li><p>Nested CSS classes in Sass files may not work as expected with CSS Modules</p>
</li>
<li><p>Class names must be camelCase (no dashes)</p>
</li>
<li><p>File names need the <code>.module.scss</code> extension</p>
</li>
</ul>
<p>For a documentation site with custom branding, you might opt for standard Sass files instead of CSS Modules to avoid these constraints.</p>
<h2 id="heading-adding-swagger-api-documentation">Adding Swagger API Documentation</h2>
<p>One of the more challenging aspects of developer documentation is incorporating API references. Here's how to integrate Swagger specifications:</p>
<h3 id="heading-setup">Setup</h3>
<p>Swagger generates documentation in JSON and YAML formats. To display these specs, use the <code>swagger-ui-react</code> component library:</p>
<pre><code class="lang-bash">npm install --save swagger-ui-react
</code></pre>
<h3 id="heading-handling-server-side-rendering-issues">Handling Server-Side Rendering Issues</h3>
<p>Swagger UI doesn't support server-side rendering (SSR), which causes build failures with Gatsby. Here's a workaround using lazy loading:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SwaggerContainer = <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> { slug } = props;
  <span class="hljs-keyword">const</span> [file, setFile] = React.useState();

  <span class="hljs-comment">// Only load swagger-ui-react on client side</span>
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span>) {
    <span class="hljs-keyword">if</span> (!file) {
      getFile(slug)
        .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> setFile(data))
        .catch(<span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> <span class="hljs-built_in">console</span>.error);
    }

    <span class="hljs-keyword">const</span> SwaggerUI = React.lazy(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"swagger-ui-react"</span>));

    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.Suspense</span> <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">div</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">SwaggerUI</span> <span class="hljs-attr">spec</span>=<span class="hljs-string">{file}</span> <span class="hljs-attr">docExpansion</span>=<span class="hljs-string">"list"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">React.Suspense</span>&gt;</span></span>
    );
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }
};
</code></pre>
<h3 id="heading-customizing-swagger-display">Customizing Swagger Display</h3>
<p>Control how your API documentation appears:</p>
<ul>
<li><p><strong>docExpansion="list"</strong>: Expand operations only</p>
</li>
<li><p><strong>docExpansion="full"</strong>: Expand all sections</p>
</li>
<li><p><strong>docExpansion="none"</strong>: Collapse everything (default)</p>
</li>
</ul>
<p><strong>Note</strong>: Operations without explicit tags will appear under "default" in Swagger UI.</p>
<h3 id="heading-fixing-jest-test-issues">Fixing Jest Test Issues</h3>
<p>After implementing lazy loading, you might encounter Jest test failures related to dynamic imports. Fix this by adding Babel support:</p>
<pre><code class="lang-bash">npm install --save @babel/plugin-syntax-dynamic-import
</code></pre>
<p>Create a <code>.babelrc</code> file:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"presets"</span>: [<span class="hljs-string">"babel-preset-gatsby"</span>],
  <span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"@babel/plugin-syntax-dynamic-import"</span>],
  <span class="hljs-attr">"env"</span>: {
    <span class="hljs-attr">"test"</span>: {
      <span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"dynamic-import-node"</span>]
    }
  }
}
</code></pre>
<h2 id="heading-seo-optimization">SEO Optimization</h2>
<p>Don't forget to add proper metadata to your pages for better SEO:</p>
<pre><code class="lang-bash">npm install --save gatsby-plugin-react-helmet react-helmet
</code></pre>
<p>React Helmet allows you to manage document head tags, including titles, meta descriptions, and Open Graph tags.</p>
<h2 id="heading-final-tech-stack-summary">Final Tech Stack Summary</h2>
<p>Here's what powers our documentation site:</p>
<ul>
<li><p><strong>Gatsby</strong>: Static site generator and React framework</p>
</li>
<li><p><strong>Sass</strong>: CSS preprocessing for maintainable styles</p>
</li>
<li><p><strong>Markdown with Prism.js</strong>: Content creation with beautiful syntax highlighting</p>
</li>
<li><p><strong>Swagger UI React</strong>: Interactive API documentation</p>
</li>
<li><p><strong>React Helmet</strong>: SEO and metadata management</p>
</li>
<li><p><strong>Custom branding</strong>: No off-the-shelf themes, complete design control</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building a documentation website with Gatsby provides a perfect balance of developer experience and end-user performance. The JAMstack architecture ensures your docs load instantly, while Gatsby's plugin ecosystem handles everything from Markdown processing to API documentation.</p>
<p>The initial setup requires navigating some SSR gotchas (especially with Swagger), but the result is a fast, maintainable documentation site that scales beautifully. Whether you're documenting a small library or a complex API platform, Gatsby gives you the tools to create an exceptional documentation experience.</p>
<p>Ready to get started? Check out the <a target="_blank" href="https://www.gatsbyjs.com/docs/tutorial/">Gatsby tutorials</a> and start building your documentation site today!</p>
]]></content:encoded></item></channel></rss>