<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/styles.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>JavaScript Development Space - Master JS and NodeJS</title><description>Explore the world of JavaScript at our blog, your ultimate resource for guides, tutorials, and articles. Uncover the latest insights, tips, and trends.</description><link>https://jsdev.space/</link><language>en-us</language><item><title>Build a Minimal Crypto Dashboard with CCXT, CoinGecko and React</title><link>https://jsdev.space/minimal-crypto-dashboard/</link><guid isPermaLink="true">https://jsdev.space/minimal-crypto-dashboard/</guid><description>Build a compact crypto dashboard with live prices, CoinGecko fallback, OHLCV candles, React Query and a monochrome table/chart layout.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In this tutorial, we are going to build a small crypto dashboard that shows live coin prices in a compact table and displays a candlestick chart for the selected coin.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use &lt;a href=&quot;https://github.com/ccxt/ccxt&quot;&gt;CCXT&lt;/a&gt; as the main market data source;&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://www.coingecko.com/&quot;&gt;CoinGecko&lt;/a&gt; as a fallback when the exchange does not have a required pair;&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;fetchOHLCV&lt;/code&gt; to load candlestick data;&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://tanstack.com/query/v3&quot;&gt;React Query&lt;/a&gt; to refresh prices automatically;&lt;/li&gt;
&lt;li&gt;render candles with &lt;a href=&quot;https://github.com/tradingview/lightweight-charts&quot;&gt;Lightweight Charts&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;make the UI clean, compact and monochrome.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the end, we will have a small dashboard where the table sits on the left, the chart sits on the right, and clicking a coin updates the active chart.&lt;/p&gt;
&lt;p&gt;The complete source code for this project is available on &lt;a href=&quot;https://github.com/jsdevspace/Minimal-Crypto-Dashboard&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 1: Create the Project&lt;/h2&gt;
&lt;p&gt;Start with a React + TypeScript Vite project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create vite@latest crypto-dashboard -- --template react-ts
cd crypto-dashboard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the packages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install ccxt express cors @tanstack/react-query lightweight-charts concurrently tsx
npm install -D @types/express @types/cors
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ccxt&lt;/code&gt; for exchange market data;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;express&lt;/code&gt; for the local API;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cors&lt;/code&gt; so the React app can call the API;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@tanstack/react-query&lt;/code&gt; for polling and caching;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lightweight-charts&lt;/code&gt; for the candlestick chart;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;concurrently&lt;/code&gt; to run frontend and backend together;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tsx&lt;/code&gt; to run the TypeScript server directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 2: Add Scripts&lt;/h2&gt;
&lt;p&gt;Update &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;concurrently \&quot;npm run dev:client\&quot; \&quot;npm run dev:server\&quot;&quot;,
    &quot;dev:client&quot;: &quot;vite&quot;,
    &quot;dev:server&quot;: &quot;tsx watch server/index.ts&quot;,
    &quot;build&quot;: &quot;tsc -b &amp;amp;&amp;amp; vite build&quot;,
    &quot;preview&quot;: &quot;vite preview&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now one command starts both the frontend and the backend:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 3: Create the Backend API&lt;/h2&gt;
&lt;p&gt;Create a new file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server/index.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the base Express server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import express from &quot;express&quot;;
import cors from &quot;cors&quot;;
import * as ccxt from &quot;ccxt&quot;;

const app = express();

app.use(cors());

const exchange = new ccxt.bitget({
  enableRateLimit: true,
});

app.listen(4000, () =&amp;gt; {
  console.log(&quot;API running on http://localhost:4000&quot;);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using Bitget here because it works well for public market data and does not require an API key for tickers or candles.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 4: Define the Watchlist&lt;/h2&gt;
&lt;p&gt;Add a small watchlist:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type WatchAsset = {
  symbol: string;
  pair: string;
  geckoId: string;
};

const watchlist: WatchAsset[] = [
  { symbol: &quot;BTC&quot;, pair: &quot;BTC/USDT&quot;, geckoId: &quot;bitcoin&quot; },
  { symbol: &quot;ETH&quot;, pair: &quot;ETH/USDT&quot;, geckoId: &quot;ethereum&quot; },
  { symbol: &quot;SOL&quot;, pair: &quot;SOL/USDT&quot;, geckoId: &quot;solana&quot; },
  { symbol: &quot;DOGE&quot;, pair: &quot;DOGE/USDT&quot;, geckoId: &quot;dogecoin&quot; },
  { symbol: &quot;TAO&quot;, pair: &quot;TAO/USDT&quot;, geckoId: &quot;bittensor&quot; }
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each coin has three fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;symbol&lt;/code&gt; is what we show in the UI;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pair&lt;/code&gt; is what CCXT uses;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;geckoId&lt;/code&gt; is what CoinGecko uses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This small mapping is important because exchanges and CoinGecko do not use the same identifiers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 5: Add CoinGecko Fallback&lt;/h2&gt;
&lt;p&gt;Sometimes a coin exists on CoinGecko but does not have a liquid pair on the exchange we use. Instead of failing, we can fallback to CoinGecko.&lt;/p&gt;
&lt;p&gt;Add this helper:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function getCoinGeckoPrices(ids: string[]) {
  if (ids.length === 0) return {};

  const url = new URL(&quot;https://api.coingecko.com/api/v3/simple/price&quot;);

  url.searchParams.set(&quot;ids&quot;, [...ids, &quot;tether&quot;].join(&quot;,&quot;));
  url.searchParams.set(&quot;vs_currencies&quot;, &quot;usd&quot;);
  url.searchParams.set(&quot;include_24hr_change&quot;, &quot;true&quot;);
  url.searchParams.set(&quot;include_24hr_vol&quot;, &quot;true&quot;);

  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(&quot;CoinGecko request failed&quot;);
  }

  const data = await response.json();
  const tetherUsd = data.tether?.usd ?? 1;

  return Object.fromEntries(
    Object.entries(data).map(([id, value]: any) =&amp;gt; [
      id,
      {
        price: value.usd / tetherUsd,
        change24h: value.usd_24h_change ?? null,
        volume: value.usd_24h_vol ?? null
      }
    ])
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CoinGecko returns prices in USD. Exchange pairs usually come in USDT. For a small dashboard, USD and USDT are often close enough, but we can still normalize CoinGecko values by dividing them by the current Tether price.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 6: Create the Prices Endpoint&lt;/h2&gt;
&lt;p&gt;Now add &lt;code&gt;/api/prices&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&quot;/api/prices&quot;, async (_req, res) =&amp;gt; {
  try {
    await exchange.loadMarkets();

    const pairs = watchlist.map((asset) =&amp;gt; asset.pair);
    const tickers = await exchange.fetchTickers(pairs);

    const missingGeckoIds: string[] = [];

    for (const asset of watchlist) {
      if (!tickers[asset.pair]?.last) {
        missingGeckoIds.push(asset.geckoId);
      }
    }

    const geckoPrices = await getCoinGeckoPrices(missingGeckoIds);

    const prices = watchlist.map((asset) =&amp;gt; {
      const ticker = tickers[asset.pair];
      const gecko = geckoPrices[asset.geckoId] as any;

      return {
        symbol: asset.symbol,
        pair: asset.pair,
        source: ticker?.last ? &quot;ccxt&quot; : &quot;coingecko&quot;,
        price: ticker?.last ?? gecko?.price ?? null,
        high24h: ticker?.high ?? null,
        low24h: ticker?.low ?? null,
        change24h: ticker?.percentage ?? gecko?.change24h ?? null,
        volume: ticker?.quoteVolume ?? gecko?.volume ?? null
      };
    });

    res.json(prices);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: &quot;Failed to load prices&quot; });
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The flow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load exchange markets.&lt;/li&gt;
&lt;li&gt;Request tickers from CCXT.&lt;/li&gt;
&lt;li&gt;Find missing pairs.&lt;/li&gt;
&lt;li&gt;Request missing prices from CoinGecko.&lt;/li&gt;
&lt;li&gt;Merge both sources into one response.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The frontend does not need to know how complicated the data fetching is. It receives one clean array.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 7: Add Candlestick Data with &lt;code&gt;fetchOHLCV&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;For the chart, add another endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&quot;/api/candles/:symbol&quot;, async (req, res) =&amp;gt; {
  try {
    await exchange.loadMarkets();

    const coin = req.params.symbol.toUpperCase();
    const pair = `${coin}/USDT`;

    if (!exchange.markets[pair]) {
      return res.status(404).json({
        error: `No ${pair} market on Bitget`
      });
    }

    const timeframe = String(req.query.timeframe ?? &quot;1m&quot;);
    const limit = Number(req.query.limit ?? 120);

    const candles = await exchange.fetchOHLCV(
      pair,
      timeframe,
      undefined,
      limit
    );

    const result = candles.map(([time, open, high, low, close, volume]) =&amp;gt; ({
      time: Math.floor(time / 1000),
      open,
      high,
      low,
      close,
      volume
    }));

    res.json(result);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: &quot;Failed to load candles&quot; });
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;fetchOHLCV&lt;/code&gt; returns arrays in this format:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
  timestamp,
  open,
  high,
  low,
  close,
  volume
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lightweight Charts expects candle data as objects, so we convert the array into a cleaner structure.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 8: Create Frontend API Helpers&lt;/h2…</content:encoded></item><item><title>Howto Find the Start of a Cycle in a Linked List</title><link>https://jsdev.space/howto/linked-list-cycle-start/</link><guid isPermaLink="true">https://jsdev.space/howto/linked-list-cycle-start/</guid><description>Learn how Floyd’s Fast and Slow Pointer algorithm detects cycles and finds the exact starting node of a loop in O(n) time and O(1) space.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Linked lists are one of the most common data structures used in coding interviews, and cycle detection is one of the most frequently asked problems.&lt;/p&gt;
&lt;p&gt;At first glance, the challenge seems simple: determine whether a linked list contains a loop. However, many interviewers take the problem one step further and ask you to find the exact node where the cycle begins.&lt;/p&gt;
&lt;p&gt;The naive solution is to store every visited node inside a hash set and check whether you&apos;ve seen a node before. While that works, it requires additional memory.&lt;/p&gt;
&lt;p&gt;A much more elegant solution uses Floyd’s Cycle Detection Algorithm, also known as the &lt;strong&gt;Tortoise and Hare Algorithm&lt;/strong&gt;. This approach requires no extra storage and can both detect a cycle and locate its starting point.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Given the head of a singly linked list, return the node where a cycle begins.&lt;/p&gt;
&lt;p&gt;If no cycle exists, return &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input:
3 → 2 → 0 → -4
    ↑       ↓
    └───────┘

Output:
Node with value 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input:
1 → 2
↑   ↓
└───┘

Output:
Node with value 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 3&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input:
1 → null

Output:
null
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why a Normal Traversal Doesn&apos;t Work&lt;/h2&gt;
&lt;p&gt;In a regular linked list, traversal eventually reaches &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let currentNode = head;

while (currentNode) {
  currentNode = currentNode.next;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a cycle exists, the traversal never terminates.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 → 2 → 3 → 4
    ↑       ↓
    └───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pointer keeps moving forever.&lt;/p&gt;
&lt;p&gt;This means we need a smarter approach.&lt;/p&gt;
&lt;h2&gt;Floyd&apos;s Fast and Slow Pointer Technique&lt;/h2&gt;
&lt;p&gt;The core idea is simple.&lt;/p&gt;
&lt;p&gt;Create two pointers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;slowRunner&lt;/code&gt; moves one node at a time&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fastRunner&lt;/code&gt; moves two nodes at a time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the list contains a cycle, the faster pointer will eventually catch the slower pointer.&lt;/p&gt;
&lt;p&gt;If the list has no cycle, the fast pointer will reach the end of the list.&lt;/p&gt;
&lt;h3&gt;Visual Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1 → 2 → 3 → 4 → 5
        ↑       ↓
        └───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iteration 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slowRunner = 2
fastRunner = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iteration 2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slowRunner = 3
fastRunner = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iteration 3:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slowRunner = 4
fastRunner = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pointers meet.&lt;/p&gt;
&lt;p&gt;Once they meet, we know a cycle exists.&lt;/p&gt;
&lt;h2&gt;Finding the Entry Point of the Cycle&lt;/h2&gt;
&lt;p&gt;Detecting the cycle is only half of the problem.&lt;/p&gt;
&lt;p&gt;The next challenge is determining where the loop begins.&lt;/p&gt;
&lt;p&gt;After the first meeting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Leave one pointer at the meeting point.&lt;/li&gt;
&lt;li&gt;Move another pointer back to the head.&lt;/li&gt;
&lt;li&gt;Advance both pointers one step at a time.&lt;/li&gt;
&lt;li&gt;The node where they meet again is the cycle entry.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Why Does This Work?&lt;/h3&gt;
&lt;p&gt;Assume:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a&lt;/code&gt; = distance from head to cycle entry&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt; = cycle length&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c&lt;/code&gt; = distance from cycle entry to the meeting point&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the two pointers meet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;slow distance = a + c

fast distance = a + c + k × b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the fast pointer moves twice as quickly:&lt;/p&gt;
&lt;p&gt;2(a+c)=a+c+kb&lt;/p&gt;
&lt;p&gt;Rearranging gives:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = kb - c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means the distance from the head to the cycle entry is exactly equal to the distance from the meeting point back to the cycle entry when moving around the loop.&lt;/p&gt;
&lt;p&gt;As a result, one pointer starting from the head and another starting from the meeting point will arrive at the cycle entry simultaneously.&lt;/p&gt;
&lt;h2&gt;JavaScript Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Definition for singly linked list.
 * function ListNode(value, nextNode) {
 *   this.val = value ?? 0;
 *   this.next = nextNode ?? null;
 * }
 */

/**
 * @param {ListNode} head
 * @returns {ListNode | null}
 */
function findCycleStart(head) {
  let slowRunner = head;
  let fastRunner = head;

  while (fastRunner &amp;amp;&amp;amp; fastRunner.next) {
    slowRunner = slowRunner.next;
    fastRunner = fastRunner.next.next;

    if (slowRunner === fastRunner) {
      let pointerFromHead = head;
      let pointerFromMeeting = slowRunner;

      while (pointerFromHead !== pointerFromMeeting) {
        pointerFromHead = pointerFromHead.next;
        pointerFromMeeting = pointerFromMeeting.next;
      }

      return pointerFromHead;
    }
  }

  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step-by-Step Walkthrough&lt;/h2&gt;
&lt;p&gt;Consider the following linked list:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;3 → 2 → 0 → -4
    ↑       ↓
    └───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Phase 1: Detect the Cycle&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;slowRunner:
3 → 2 → 0 → -4 → 2

fastRunner:
3 → 0 → 2 → -4 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually both pointers meet inside the loop.&lt;/p&gt;
&lt;h3&gt;Phase 2: Locate the Entry&lt;/h3&gt;
&lt;p&gt;Reset one pointer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pointerFromHead = 3
pointerFromMeeting = meeting node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move both one step at a time:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pointerFromHead:
3 → 2

pointerFromMeeting:
0 → -4 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both arrive at node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That node is returned as the cycle entry.&lt;/p&gt;
&lt;h2&gt;Alternative Solution Using a Hash Set&lt;/h2&gt;
&lt;p&gt;Another common approach stores every visited node.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function findCycleStart(head) {
  const visitedNodes = new Set();

  let currentNode = head;

  while (currentNode) {
    if (visitedNodes.has(currentNode)) {
      return currentNode;
    }

    visitedNodes.add(currentNode);
    currentNode = currentNode.next;
  }

  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Easy to understand&lt;/li&gt;
&lt;li&gt;Easy to implement&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Requires additional memory&lt;/li&gt;
&lt;li&gt;Space complexity becomes O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For interview settings, Floyd&apos;s algorithm is generally preferred.&lt;/p&gt;
&lt;h2&gt;Complexity Analysis&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time Complexity&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Space Complexity&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The algorithm performs at most two traversals:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detecting the cycle.&lt;/li&gt;
&lt;li&gt;Finding the cycle entry.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No extra data structures are required.&lt;/p&gt;
&lt;h2&gt;Common Interview Questions&lt;/h2&gt;
&lt;h3&gt;Why do the pointers always meet when a cycle exists?&lt;/h3&gt;
&lt;p&gt;Because the fast pointer gains one node on the slow pointer during each iteration inside the loop. Eventually it catches up.&lt;/p&gt;
&lt;h3&gt;Why reset one pointer to the head?&lt;/h3&gt;
&lt;p&gt;The mathematical relationship between the meeting point and cycle entry guarantees that both pointers will reach the entry node at the same time when moving at equal speed.&lt;/p&gt;
&lt;h3&gt;Can this problem be solved without Floyd&apos;s algorithm?&lt;/h3&gt;
&lt;p&gt;Yes. A hash set can track visited nodes.&lt;/p&gt;
&lt;p&gt;However:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Hash Set:
Time  = O(n)
Space = O(n)

Floyd:
Time  = O(n)
Space = O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Floyd solution is more efficient.&lt;/p&gt;
&lt;h3&gt;What prevents the fast pointer from causing errors?&lt;/h3&gt;
&lt;p&gt;The loop condition ensures safe access:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (fastRunner &amp;amp;&amp;amp; fastRunner.next)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This guarantees that &lt;code&gt;fastRunner.next.next&lt;/code&gt; is only accessed when valid.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Finding the start of a cycle in a linked list is one of the most important linked-list interview problems. The challenge combines pointer manipulation, algorithmic thinking, and a clever mathematical insight.&lt;/p&gt;
&lt;p&gt;The key idea is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a slow pointer moving one step at a time.&lt;/li&gt;
&lt;li&gt;Use a fast pointer moving two steps at a time.&lt;/li&gt;
&lt;li&gt;Detect the meeting point.&lt;/li&gt;
&lt;li&gt;Reset one pointer to the head.&lt;/li&gt;
&lt;li&gt;Move both pointers at the same speed.&lt;/li&gt;
&lt;li&gt;Their next meeting point is the beginning of the cycle.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This elegant technique runs in &lt;strong&gt;O(n)&lt;/strong&gt; time, uses &lt;strong&gt;O(1)&lt;/strong&gt; extra space, and is considered the standard soluti…</content:encoded></item><item><title>How to Build Smooth Scrolling Interfaces with CSS Scroll Snap</title><link>https://jsdev.space/howto/css-scroll-snap-smooth-scrolling/</link><guid isPermaLink="true">https://jsdev.space/howto/css-scroll-snap-smooth-scrolling/</guid><description>Learn how to use CSS Scroll Snap to build smooth carousels, galleries, and full-page scrolling layouts with clean, performant CSS..</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Master native scroll snapping in CSS to create smooth sliders, paginated sections, and polished scrolling experiences without heavy JavaScript libraries.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Modern interfaces rely heavily on scrolling.&lt;/p&gt;
&lt;p&gt;Horizontal product sliders, onboarding screens, fullscreen storytelling pages, mobile galleries, and swipeable dashboards all depend on movement that feels deliberate and predictable. Users expect content to land neatly into place instead of stopping awkwardly between elements.&lt;/p&gt;
&lt;p&gt;For years, developers solved this with JavaScript libraries, custom event listeners, and complicated calculations. That approach works, but it often creates extra maintenance, accessibility problems, and performance issues on mobile devices.&lt;/p&gt;
&lt;p&gt;CSS Scroll Snap gives us a better option.&lt;/p&gt;
&lt;p&gt;Instead of manually calculating scroll positions, you can define snap points directly in CSS and let the browser handle the alignment. The result is cleaner code, better performance, and a smoother user experience.&lt;/p&gt;
&lt;p&gt;In this guide, you will learn how CSS Scroll Snap works, how to configure it properly, and how to use it in real production scenarios such as carousels, galleries, and full-page layouts.&lt;/p&gt;
&lt;h2&gt;What Is CSS Scroll Snap?&lt;/h2&gt;
&lt;p&gt;CSS Scroll Snap is a native CSS feature that controls how scrolling behaves inside a scrollable container.&lt;/p&gt;
&lt;p&gt;You define a scrollable parent, configure the snapping behavior, and then tell child elements where they should align when scrolling stops. Once those rules are in place, the browser automatically moves the scroll position toward the closest snap point.&lt;/p&gt;
&lt;p&gt;Think of it like invisible magnetic points attached to your layout. Instead of the scroll ending in a random position, the content lands exactly where you want it.&lt;/p&gt;
&lt;p&gt;This is useful for interfaces where visual rhythm matters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;horizontal carousels&lt;/li&gt;
&lt;li&gt;image galleries&lt;/li&gt;
&lt;li&gt;onboarding screens&lt;/li&gt;
&lt;li&gt;fullscreen page sections&lt;/li&gt;
&lt;li&gt;mobile navigation layouts&lt;/li&gt;
&lt;li&gt;step-based tutorials&lt;/li&gt;
&lt;li&gt;dashboard panels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best part is that many of these patterns can be created without a slider library.&lt;/p&gt;
&lt;h2&gt;The Basic Structure&lt;/h2&gt;
&lt;p&gt;Every CSS Scroll Snap layout has two main parts.&lt;/p&gt;
&lt;p&gt;First, you need a scroll container. This element must have overflow enabled and a &lt;code&gt;scroll-snap-type&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;Second, you need snap children. These elements usually use &lt;code&gt;scroll-snap-align&lt;/code&gt; to define where they should lock into place.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.snap-container {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.snap-item {
  scroll-snap-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This small amount of CSS already creates a basic horizontal snapping layout.&lt;/p&gt;
&lt;p&gt;The container says: “scroll horizontally and snap strictly.”&lt;/p&gt;
&lt;p&gt;Each child says: “when snapping happens, align me to the center.”&lt;/p&gt;
&lt;h2&gt;Understanding &lt;code&gt;scroll-snap-type&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The most important property is &lt;code&gt;scroll-snap-type&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It tells the browser two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which axis should snap&lt;/li&gt;
&lt;li&gt;how strict snapping should be&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The syntax looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.gallery-track {
  scroll-snap-type: x mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first value controls direction.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;x&lt;/code&gt; for horizontal snapping:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.product-row {
  scroll-snap-type: x mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;y&lt;/code&gt; for vertical snapping:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.page-sections {
  scroll-snap-type: y mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;both&lt;/code&gt; when the container can scroll in both directions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.canvas-grid {
  scroll-snap-type: both proximity;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use logical values such as &lt;code&gt;block&lt;/code&gt; and &lt;code&gt;inline&lt;/code&gt;, which follow the document writing mode. For most everyday layouts, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are easier to understand and maintain.&lt;/p&gt;
&lt;h2&gt;Mandatory vs Proximity Snapping&lt;/h2&gt;
&lt;p&gt;The second value controls how strongly the browser should snap.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;mandatory&lt;/code&gt; value forces the scroll position to land on a snap point.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.carousel {
  scroll-snap-type: x mandatory;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful when precision matters. Carousels, onboarding screens, and page-by-page interfaces usually feel better with mandatory snapping because every movement has a clear destination.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;proximity&lt;/code&gt; value is softer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.gallery {
  scroll-snap-type: x proximity;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With proximity snapping, the browser only snaps when the final scroll position is already close to a snap point. This feels more natural for galleries or layouts where users may want a little more freedom.&lt;/p&gt;
&lt;p&gt;A good rule is simple: use &lt;code&gt;mandatory&lt;/code&gt; for strict step-based interfaces and &lt;code&gt;proximity&lt;/code&gt; for exploratory browsing.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;scroll-snap-align&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Once the container knows how to snap, the child elements need to define where they should align.&lt;/p&gt;
&lt;p&gt;That is the job of &lt;code&gt;scroll-snap-align&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.feature-card {
  scroll-snap-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Common values include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;center&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;end&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use &lt;code&gt;start&lt;/code&gt; when each item should align with the beginning of the scroll container.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.chapter {
  scroll-snap-align: start;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is common in full-page vertical layouts where every section should begin at the top of the viewport.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;center&lt;/code&gt; when the item should land in the middle.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.showcase-slide {
  scroll-snap-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works well for carousels, product cards, and image previews.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;end&lt;/code&gt; less often, but it can be helpful for right-aligned panels or special reading layouts.&lt;/p&gt;
&lt;h2&gt;Building a CSS-Only Product Carousel&lt;/h2&gt;
&lt;p&gt;Let’s build a practical carousel using only HTML and CSS.&lt;/p&gt;
&lt;p&gt;This example is intentionally simple, but the structure is close to what you might use in a real interface.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;section class=&quot;product-carousel&quot; aria-label=&quot;Featured products&quot;&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Mechanical Keyboard&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Studio Monitor&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Wireless Mouse&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;USB-C Dock&amp;lt;/article&amp;gt;
  &amp;lt;article class=&quot;product-card&quot;&amp;gt;Laptop Stand&amp;lt;/article&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add the CSS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.product-carousel {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  padding: 24px;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.product-carousel::-webkit-scrollbar {
  display: none;
}

.product-card {
  flex: 0 0 320px;
  height: 220px;
  display: grid;
  place-items: center;
  border-radius: 18px;
  color: white;
  font-size: 1.4rem;
  font-weight: 700;
  scroll-snap-align: center;
  background: linear-gradient(135deg, #2563eb, #7c3aed);
  box-shadow: 0 16px 40px rgb(15 23 42 / 18%);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you a clean horizontal carousel.&lt;/p&gt;
&lt;p&gt;The container uses &lt;code&gt;overflow-x: auto&lt;/code&gt;, which creates the horizontal scrolling area. The &lt;code&gt;scroll-snap-type: x mandatory&lt;/code&gt; rule tells the browser to snap horizontally. Each card uses &lt;code&gt;scroll-snap-align: center&lt;/code&gt;, so cards land in the middle of the visible area.&lt;/p&gt;
&lt;p&gt;There is no custom JavaScript and no dependency on a carousel package.&lt;/p&gt;
&lt;h2&gt;Improving the Carousel for Real Projects&lt;/h2&gt;
&lt;p&gt;A real carousel often needs better spacing on small screens. You can adjust the card width with &lt;code&gt;clamp()&lt;/code&gt; so the layout adapts naturally.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.product-card {
  flex: 0 0 clamp(240px, 75vw, 360px);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means the card …</content:encoded></item><item><title>Next.js App Router Caching: Why Your Data Stayed Stale</title><link>https://jsdev.space/nextjs-app-cache/</link><guid isPermaLink="true">https://jsdev.space/nextjs-app-cache/</guid><description>Understand how caching actually behaves in the Next.js App Router and learn practical ways to debug force-cache, no-store, and revalidate.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Caching in Next.js usually becomes confusing for the same reason: several different systems can produce almost identical behavior.&lt;/p&gt;
&lt;p&gt;Your API already returns new data.&lt;/p&gt;
&lt;p&gt;You refresh the page.&lt;/p&gt;
&lt;p&gt;The UI still shows an older value.&lt;/p&gt;
&lt;p&gt;You immediately add:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fetch(endpoint, { cache: &quot;no-store&quot; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything starts working.&lt;/p&gt;
&lt;p&gt;Problem solved.&lt;/p&gt;
&lt;p&gt;Until the next question appears:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If every request bypasses caching, why does Next.js ship with an entire caching model in the first place?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The answer is simpler than it first looks.&lt;/p&gt;
&lt;p&gt;In App Router, “caching” is not one feature.&lt;/p&gt;
&lt;p&gt;Different layers can influence what you see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client navigation reuse&lt;/li&gt;
&lt;li&gt;route rendering behavior&lt;/li&gt;
&lt;li&gt;server-side fetch caching&lt;/li&gt;
&lt;li&gt;time-based revalidation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you treat them as one mechanism, debugging quickly becomes frustrating.&lt;/p&gt;
&lt;p&gt;The easiest way to understand caching is not through diagrams or definitions.&lt;/p&gt;
&lt;p&gt;It is through observation.&lt;/p&gt;
&lt;h2&gt;Step One: Detect Whether the Server Rendered Again&lt;/h2&gt;
&lt;p&gt;Before debugging data, verify whether the page actually executed on the server.&lt;/p&gt;
&lt;p&gt;Create a small render indicator.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/components/ServerRenderBadge.tsx

type ServerRenderBadgeProps = {
  name?: string;
};

export function ServerRenderBadge({
  name = &quot;server render&quot;,
}: ServerRenderBadgeProps) {
  const serverRenderedAt = new Date().toISOString();

  return (
    &amp;lt;p className=&quot;text-xs text-slate-500&quot;&amp;gt;
      {name}: &amp;lt;code&amp;gt;{serverRenderedAt}&amp;lt;/code&amp;gt;
    &amp;lt;/p&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component runs on the server.&lt;/p&gt;
&lt;p&gt;Every time the route truly renders again, the timestamp changes.&lt;/p&gt;
&lt;p&gt;Use it inside multiple routes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/catalog/page.tsx

&amp;lt;ServerRenderBadge name=&quot;/catalog render&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// app/catalog/[id]/page.tsx

&amp;lt;ServerRenderBadge name=&quot;/catalog/[id] render&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now perform a simple navigation test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;/catalog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Visit an item page&lt;/li&gt;
&lt;li&gt;Press Back&lt;/li&gt;
&lt;li&gt;Open the same item again&lt;/li&gt;
&lt;li&gt;Reload the browser tab&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You will notice an important distinction.&lt;/p&gt;
&lt;p&gt;Back navigation may preserve the same timestamp.&lt;/p&gt;
&lt;p&gt;Reload often produces a new one.&lt;/p&gt;
&lt;p&gt;That alone reveals something important:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;seeing a page again does not automatically mean the server rendered it again.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Already, you have separated navigation behavior from rendering behavior.&lt;/p&gt;
&lt;h2&gt;Rendering Freshness and Data Freshness Are Different Signals&lt;/h2&gt;
&lt;p&gt;Knowing whether a render happened is useful.&lt;/p&gt;
&lt;p&gt;But most debugging sessions are really about something else:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Did a fresh API response arrive?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Add a second marker inside your data layer.&lt;/p&gt;
&lt;p&gt;Instead of exposing only JSON, return debugging metadata together with the response.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/lib/catalog-api.ts

const CATALOG_API_URL = &quot;https://dummyjson.com&quot;;

type RequestDebugInfo&amp;lt;T&amp;gt; = T &amp;amp; {
  _debug: {
    receivedAt: string;
    cacheTtl?: number;
    validUntil?: string;
  };
};

async function requestCatalog&amp;lt;T&amp;gt;(
  path: string,
  init?: RequestInit &amp;amp; {
    next?: {
      revalidate?: number;
    };
  }
): Promise&amp;lt;RequestDebugInfo&amp;lt;T&amp;gt;&amp;gt; {
  const response = await fetch(
    `${CATALOG_API_URL}${path}`,
    init
  );

  if (!response.ok) {
    throw new Error(
      `Catalog request failed: ${response.status}`
    );
  }

  const receivedAt =
    response.headers.get(&quot;date&quot;) ??
    new Date().toUTCString();

  const payload = await response.json();

  return {
    ...payload,
    _debug: {
      receivedAt,
    },
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Display the metadata alongside the render marker.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div className=&quot;space-y-2 text-sm&quot;&amp;gt;
  &amp;lt;p&amp;gt;
    API response received:
    &amp;lt;code&amp;gt;{item._debug.receivedAt}&amp;lt;/code&amp;gt;
  &amp;lt;/p&amp;gt;

  &amp;lt;ServerRenderBadge
    name=&quot;product page render&quot;
  /&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have two independent measurements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ServerRenderBadge&lt;/strong&gt; → did the route render?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;receivedAt&lt;/strong&gt; → did a new network response arrive?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That distinction changes the entire debugging experience.&lt;/p&gt;
&lt;p&gt;Because a page can absolutely render again while still serving cached data.&lt;/p&gt;
&lt;p&gt;That is usually where the “Next.js is stuck” feeling comes from.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;force-cache&lt;/code&gt;: The UI Updates, the Network Request Doesn&apos;t&lt;/h2&gt;
&lt;p&gt;Start with explicit caching.&lt;/p&gt;
&lt;p&gt;Create a reusable cache profile helper.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/lib/cache-profile.ts

type CacheMode =
  | { strategy: &quot;cached&quot; }
  | { strategy: &quot;live&quot; }
  | {
      strategy: &quot;timed&quot;;
      ttl: number;
    };

function buildFetchPolicy(
  mode: CacheMode
): RequestInit {
  switch (mode.strategy) {
    case &quot;live&quot;:
      return { cache: &quot;no-store&quot; };

    case &quot;timed&quot;:
      return {
        next: {
          revalidate: mode.ttl,
        },
      };

    default:
      return {
        cache: &quot;force-cache&quot;,
      };
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use it in your data loader.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/lib/products.ts

export async function loadProductDetails(
  productId: string
) {
  return requestCatalog(
    `/products/${encodeURIComponent(
      productId
    )}`,
    buildFetchPolicy({
      strategy: &quot;cached&quot;,
    })
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build the application and run production mode.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run build
npm start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now reload the same page several times.&lt;/p&gt;
&lt;p&gt;You may observe something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;product page render:
13:42:18

API response received:
13:37:04
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reload again:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;product page render:
13:42:33

API response received:
13:37:04
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The render changed.&lt;/p&gt;
&lt;p&gt;The response timestamp did not.&lt;/p&gt;
&lt;p&gt;That tells you exactly what happened.&lt;/p&gt;
&lt;p&gt;The route executed again.&lt;/p&gt;
&lt;p&gt;The fetch layer reused cached data.&lt;/p&gt;
&lt;p&gt;This is not broken behavior.&lt;/p&gt;
&lt;p&gt;It is precisely what &lt;code&gt;force-cache&lt;/code&gt; is designed to do.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;no-store&lt;/code&gt;: When Every Request Must Be Fresh&lt;/h2&gt;
&lt;p&gt;Some pages cannot tolerate stale data.&lt;/p&gt;
&lt;p&gt;Switch the loader to a live profile.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function loadProductDetails(
  productId: string
) {
  return requestCatalog(
    `/products/${productId}`,
    buildFetchPolicy({
      strategy: &quot;live&quot;,
    })
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test reload behavior again.&lt;/p&gt;
&lt;p&gt;Now the signals move together.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render:
14:02:11

received:
14:02:11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render:
14:02:19

received:
14:02:19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every request performs a new fetch.&lt;/p&gt;
&lt;p&gt;This is useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;account balances&lt;/li&gt;
&lt;li&gt;order states&lt;/li&gt;
&lt;li&gt;admin dashboards&lt;/li&gt;
&lt;li&gt;user-specific operational data&lt;/li&gt;
&lt;li&gt;real-time internal tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But freshness comes with a cost.&lt;/p&gt;
&lt;p&gt;More requests.&lt;/p&gt;
&lt;p&gt;More external API pressure.&lt;/p&gt;
&lt;p&gt;Lower reuse efficiency.&lt;/p&gt;
&lt;p&gt;For many public pages, this is unnecessary overkill.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;revalidate&lt;/code&gt;: Usually the Practical Choice&lt;/h2&gt;
&lt;p&gt;Most applications need something between permanent reuse and constant refetching.&lt;/p&gt;
&lt;p&gt;That is where timed revalidation becomes valuable.&lt;/p&gt;
&lt;p&gt;Extend the response metadata.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function requestCatalog&amp;lt;T&amp;gt;(
  path: string,
  init?: RequestInit &amp;amp; {
    next?: {
      revalidate?: number;
    };
  }
) {
  const response = await fetch(
    `${CATALOG_API_URL}${path}`,
    init
  );

  const receivedAt =
    response.headers.get(&quot;date&quot;) ??
    new Date().toUTCString();

  const ttl =
    init?.next?.revalidate;

  const validUntil =
    typeof ttl === &quot;number&quot;
      ? new Date(
          Date.parse(receivedAt) +
            ttl * 1000
        ).toUTCString()
      : undefined;

  const data = await response.json();

  return {
    ...data,
    _d…</content:encoded></item><item><title>Protecting Next.js Applications in the Era of Server Actions</title><link>https://jsdev.space/server-actions-security/</link><guid isPermaLink="true">https://jsdev.space/server-actions-security/</guid><description>Modern Next.js apps need more than JWTs and middleware. Learn practical security patterns for Server Actions, React 19, Zod, RBAC, CSP, and data protection.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;The New Frontend Security Model&lt;/h2&gt;
&lt;p&gt;Frontend security used to feel relatively straightforward.&lt;/p&gt;
&lt;p&gt;A few years ago, the architecture looked familiar.&lt;/p&gt;
&lt;p&gt;You had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a SPA running in the browser&lt;/li&gt;
&lt;li&gt;a backend API somewhere else&lt;/li&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;CORS policies&lt;/li&gt;
&lt;li&gt;REST endpoints&lt;/li&gt;
&lt;li&gt;client-side state management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The boundary was obvious.&lt;/p&gt;
&lt;p&gt;Frontend lived here.&lt;/p&gt;
&lt;p&gt;Backend lived there.&lt;/p&gt;
&lt;p&gt;Security responsibilities were reasonably separated.&lt;/p&gt;
&lt;p&gt;Modern React applications no longer operate in that world.&lt;/p&gt;
&lt;p&gt;The arrival of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React Server Components&lt;/li&gt;
&lt;li&gt;Server Actions&lt;/li&gt;
&lt;li&gt;hybrid rendering&lt;/li&gt;
&lt;li&gt;Next.js App Router&lt;/li&gt;
&lt;li&gt;server-side data access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;has fundamentally changed how frontend systems behave.&lt;/p&gt;
&lt;p&gt;The application boundary has become softer.&lt;/p&gt;
&lt;p&gt;The frontend now executes logic on the server.&lt;/p&gt;
&lt;p&gt;The server now lives surprisingly close to the UI layer.&lt;/p&gt;
&lt;p&gt;And with that architectural shift comes an uncomfortable reality:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;many traditional frontend security assumptions are no longer sufficient.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The issue is not that Next.js is insecure.&lt;/p&gt;
&lt;p&gt;The issue is that developers frequently apply old security mental models to a new execution model.&lt;/p&gt;
&lt;h2&gt;Why Server Actions Quietly Changed the Threat Model&lt;/h2&gt;
&lt;p&gt;Server Actions are one of the most powerful additions to modern React development.&lt;/p&gt;
&lt;p&gt;They are also one of the easiest places to accidentally introduce vulnerabilities.&lt;/p&gt;
&lt;p&gt;At first glance, Server Actions feel magical.&lt;/p&gt;
&lt;p&gt;You write:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use server&quot;;

export async function updateProfile() {
  // server logic
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then call it directly from a form or component.&lt;/p&gt;
&lt;p&gt;No REST handler.&lt;/p&gt;
&lt;p&gt;No API folder.&lt;/p&gt;
&lt;p&gt;No explicit endpoint wiring.&lt;/p&gt;
&lt;p&gt;Extremely convenient.&lt;/p&gt;
&lt;p&gt;But convenience hides architecture.&lt;/p&gt;
&lt;p&gt;Every Server Action is effectively:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;a remotely callable server endpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That matters.&lt;/p&gt;
&lt;p&gt;Because many developers mentally treat Server Actions like private helper functions.&lt;/p&gt;
&lt;p&gt;They are not.&lt;/p&gt;
&lt;p&gt;They are part of your application&apos;s public execution surface.&lt;/p&gt;
&lt;h2&gt;The Dangerous Illusion of Hidden Server Logic&lt;/h2&gt;
&lt;p&gt;A common misunderstanding looks like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Users can only trigger this action from my form.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not necessarily.&lt;/p&gt;
&lt;p&gt;Attackers do not care about your component hierarchy.&lt;/p&gt;
&lt;p&gt;They care about callable interfaces.&lt;/p&gt;
&lt;p&gt;If an action exists and can be reached, assumptions based on UI restrictions become unreliable.&lt;/p&gt;
&lt;p&gt;Consider this example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use server&quot;;

export async function promoteUser(payload: any) {
  await db.user.update({
    where: {
      id: payload.id,
    },

    data: {
      role: payload.role,
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code seems small.&lt;/p&gt;
&lt;p&gt;Clean.&lt;/p&gt;
&lt;p&gt;Fast to ship.&lt;/p&gt;
&lt;p&gt;And deeply problematic.&lt;/p&gt;
&lt;p&gt;Problems immediately appear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;any input&lt;/li&gt;
&lt;li&gt;no session validation&lt;/li&gt;
&lt;li&gt;no authorization&lt;/li&gt;
&lt;li&gt;unrestricted role updates&lt;/li&gt;
&lt;li&gt;no ownership checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An attacker does not need your UI.&lt;/p&gt;
&lt;p&gt;They only need a way to invoke the action.&lt;/p&gt;
&lt;h2&gt;Treat Server Actions Like Production API Endpoints&lt;/h2&gt;
&lt;p&gt;The safer mental model is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;every Server Action should be treated like a hardened API endpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;validate input&lt;/li&gt;
&lt;li&gt;authenticate request context&lt;/li&gt;
&lt;li&gt;authorize resource access&lt;/li&gt;
&lt;li&gt;restrict writable fields&lt;/li&gt;
&lt;li&gt;control returned data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A stronger implementation might look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use server&quot;;

import { z } from &quot;zod&quot;;
import { getSession } from &quot;@/lib/auth&quot;;

const schema = z.object({
  userId: z.string().uuid(),
  displayName: z.string().min(2).max(80),
});

export async function updateProfile(
  rawInput: unknown
) {

  const session =
    await getSession();

  if (!session) {
    throw new Error(
      &quot;Unauthorized&quot;
    );
  }

  const input =
    schema.parse(rawInput);

  if (
    session.user.id !==
    input.userId
  ) {

    throw new Error(
      &quot;Forbidden&quot;
    );
  }

  return db.user.update({

    where: {
      id: input.userId,
    },

    data: {
      displayName:
        input.displayName,
    },

    select: {
      id: true,
      displayName: true,
      avatar: true,
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Several things changed.&lt;/p&gt;
&lt;p&gt;Input became validated.&lt;/p&gt;
&lt;p&gt;Identity became explicit.&lt;/p&gt;
&lt;p&gt;Authorization became enforced.&lt;/p&gt;
&lt;p&gt;Response leakage became restricted.&lt;/p&gt;
&lt;p&gt;That difference matters far more than syntax style.&lt;/p&gt;
&lt;h2&gt;TypeScript Is Not Runtime Security&lt;/h2&gt;
&lt;p&gt;Many frontend teams heavily rely on TypeScript.&lt;/p&gt;
&lt;p&gt;That is good.&lt;/p&gt;
&lt;p&gt;TypeScript improves developer ergonomics dramatically.&lt;/p&gt;
&lt;p&gt;But TypeScript alone does not secure Server Actions. Read &lt;a href=&quot;https://jsdev.space/typescript-security-guide/&quot;&gt;TypeScript Is Not a Security Boundary&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Because TypeScript disappears after compilation.&lt;/p&gt;
&lt;p&gt;Production JavaScript does not preserve your compile-time guarantees.&lt;/p&gt;
&lt;p&gt;If an attacker sends:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;age&quot;: &quot;DROP TABLE users&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript will not intervene.&lt;/p&gt;
&lt;p&gt;The compiler is already gone.&lt;/p&gt;
&lt;p&gt;This is where &lt;strong&gt;runtime validation&lt;/strong&gt; becomes critical.&lt;/p&gt;
&lt;h3&gt;Defense in Depth with Zod&lt;/h3&gt;
&lt;p&gt;A stronger strategy combines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compile-time guarantees&lt;/li&gt;
&lt;li&gt;runtime validation&lt;/li&gt;
&lt;li&gt;explicit business checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zod fits naturally into modern Next.js workflows.&lt;/p&gt;
&lt;p&gt;Define schemas once.&lt;/p&gt;
&lt;p&gt;Use them for validation and type inference.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;;

export const accountSchema =
  z.object({

    email:
      z.email(),

    username:
      z.string()
        .min(3)
        .max(32),

    age:
      z.number()
        .int()
        .min(13)
        .optional(),
  });

type AccountInput = z.infer&amp;lt;typeof accountSchema&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;runtime validation exists&lt;/li&gt;
&lt;li&gt;types remain synchronized&lt;/li&gt;
&lt;li&gt;schemas become self-documenting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That combination creates a meaningful defense layer.&lt;/p&gt;
&lt;p&gt;Not complete security.&lt;/p&gt;
&lt;p&gt;But stronger guarantees.&lt;/p&gt;
&lt;h3&gt;Making Server Actions Safer with &lt;code&gt;next-safe-action&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Once teams begin heavily using Server Actions, repetitive validation logic starts appearing everywhere.&lt;/p&gt;
&lt;p&gt;Typical pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;validate input
check session
parse schema
handle errors
return typed response
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Repeated dozens of times.&lt;/p&gt;
&lt;p&gt;This is exactly the problem &lt;a href=&quot;https://next-safe-action.dev/&quot;&gt;next-safe-action&lt;/a&gt; tries to solve.&lt;/p&gt;
&lt;p&gt;Instead of manually building wrappers for every action, you define a secure action client once.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { createSafeActionClient } from &quot;next-safe-action&quot;;

export const actionClient = createSafeActionClient();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then define strongly validated actions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;;

const schema = z.object({
  id: z.string().uuid(),
  name: z.string().min(2),
});

export const renameProject = actionClient.schema(schema)
  .action(
    async ({
      parsedInput,
    }) =&amp;gt; {
      return db.project.update({
        where: {
          id:
            parsedInput.id,
        },
        data: {
          name:
            parsedInput.name,
        },
      });
    }
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;validation becomes standardized&lt;/li&gt;
&lt;li&gt;parsing becomes automatic&lt;/li&gt;
&lt;li&gt;typing stays synchronized&lt;/li&gt;
&lt;li&gt;failure handling improves&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This reduces boilerplate while preserving security boundaries.&lt;/p&gt;
&lt;h2&gt;Server Components and Accidental Data Leaks&lt;/h2&gt;
&lt;p&gt;One of the most subtle problems in modern Next.js architecture is server-to-client leakage.&lt;/p&gt;
&lt;p&gt;Traditional SPAs had a relativ…</content:encoded></item><item><title>Howto Use Git Bisect to Find the Commit That Broke Everything</title><link>https://jsdev.space/howto/git-bisect/</link><guid isPermaLink="true">https://jsdev.space/howto/git-bisect/</guid><description>Something worked last week and now it’s broken. The repository has fifty new commits and nobody knows what changed.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every developer eventually runs into the same unpleasant scenario.&lt;/p&gt;
&lt;p&gt;The application worked perfectly a week ago.&lt;/p&gt;
&lt;p&gt;Now something is broken.&lt;/p&gt;
&lt;p&gt;Tests fail.&lt;/p&gt;
&lt;p&gt;The UI behaves strangely.&lt;/p&gt;
&lt;p&gt;An API endpoint returns nonsense.&lt;/p&gt;
&lt;p&gt;And during that week?&lt;/p&gt;
&lt;p&gt;Forty commits landed from multiple developers.&lt;/p&gt;
&lt;p&gt;Maybe eighty.&lt;/p&gt;
&lt;p&gt;Maybe three hundred.&lt;/p&gt;
&lt;p&gt;You could inspect commits one by one.&lt;/p&gt;
&lt;p&gt;Open diffs.&lt;/p&gt;
&lt;p&gt;Run builds.&lt;/p&gt;
&lt;p&gt;Test manually.&lt;/p&gt;
&lt;p&gt;Slowly lose your patience.&lt;/p&gt;
&lt;p&gt;Or you could let Git do the hard work.&lt;/p&gt;
&lt;p&gt;That’s exactly what git bisect exists for.&lt;/p&gt;
&lt;p&gt;Instead of checking commits sequentially, Git performs a binary search through commit history.&lt;/p&gt;
&lt;p&gt;You tell Git:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this version works&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this version is broken&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git then jumps directly to the middle of the range and asks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does it work here?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After several answers, the offending commit appears.&lt;/p&gt;
&lt;p&gt;Forty commits?&lt;/p&gt;
&lt;p&gt;Roughly six checks.&lt;/p&gt;
&lt;p&gt;Eight hundred commits?&lt;/p&gt;
&lt;p&gt;Around ten.&lt;/p&gt;
&lt;p&gt;That’s the power of binary search applied to version control.&lt;/p&gt;
&lt;h2&gt;The Core Idea: Binary Search for Broken Code&lt;/h2&gt;
&lt;p&gt;If you&apos;ve ever searched through a sorted array using binary search, the logic will feel familiar.&lt;/p&gt;
&lt;p&gt;Suppose you have twelve commits:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;c0&lt;/code&gt; worked&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c11&lt;/code&gt; is broken&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of checking:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c1 → c2 → c3 → c4 → c5 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git immediately chooses the midpoint.&lt;/p&gt;
&lt;p&gt;Something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You test it.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;c5&lt;/code&gt; works:&lt;/p&gt;
&lt;p&gt;the bad commit must be later.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;c5&lt;/code&gt; fails:&lt;/p&gt;
&lt;p&gt;the problem must be earlier.&lt;/p&gt;
&lt;p&gt;Each answer cuts the search space in half.&lt;/p&gt;
&lt;p&gt;That’s why &lt;code&gt;git bisect&lt;/code&gt; becomes incredibly efficient on large histories.&lt;/p&gt;
&lt;h2&gt;Starting a Git Bisect Session&lt;/h2&gt;
&lt;p&gt;Begin by starting a bisect session.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now tell Git:&lt;/p&gt;
&lt;p&gt;the current state is broken.&lt;/p&gt;
&lt;p&gt;Usually this means your current &lt;code&gt;HEAD&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, mark a known working commit.&lt;/p&gt;
&lt;p&gt;You can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a commit hash&lt;/li&gt;
&lt;li&gt;a branch reference&lt;/li&gt;
&lt;li&gt;a tag&lt;/li&gt;
&lt;li&gt;an older release version&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect good v1.2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, Git calculates the midpoint automatically and checks out a commit somewhere between those two states.&lt;/p&gt;
&lt;p&gt;You’ll see something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Bisecting: 4 revisions left to test
Checking out commit abc1234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your job is simple:&lt;/p&gt;
&lt;p&gt;test the application.&lt;/p&gt;
&lt;h2&gt;Testing Each Candidate Commit&lt;/h2&gt;
&lt;p&gt;Git has moved you to a candidate commit.&lt;/p&gt;
&lt;p&gt;Now answer one question:&lt;/p&gt;
&lt;p&gt;does the bug exist here?&lt;/p&gt;
&lt;p&gt;If everything works:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect good
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the bug is present:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git immediately narrows the search range and jumps again.&lt;/p&gt;
&lt;p&gt;You repeat the cycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;test&lt;/li&gt;
&lt;li&gt;mark good or bad&lt;/li&gt;
&lt;li&gt;test again&lt;/li&gt;
&lt;li&gt;mark again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventually Git reaches the answer.&lt;/p&gt;
&lt;h2&gt;When Git Finds the Guilty Commit&lt;/h2&gt;
&lt;p&gt;After several rounds, Git stops and reports the result.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a3f8c12 is the first bad commit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That commit introduced the problem.&lt;/p&gt;
&lt;p&gt;You now know exactly where to investigate.&lt;/p&gt;
&lt;p&gt;Instead of debugging forty random changes, you&apos;re examining one specific diff.&lt;/p&gt;
&lt;p&gt;That alone can save hours.&lt;/p&gt;
&lt;h2&gt;A Practical Example&lt;/h2&gt;
&lt;p&gt;Imagine this timeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A week ago:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything worked.&lt;/p&gt;
&lt;p&gt;Today:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Production is broken.&lt;/p&gt;
&lt;p&gt;Start the session:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect start
git bisect bad
git bisect good c0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git chooses the midpoint.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Checking out c5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You run the app.&lt;/p&gt;
&lt;p&gt;Suppose:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c5&lt;/code&gt; still works.&lt;/p&gt;
&lt;p&gt;Mark it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect good
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git shrinks the search window.&lt;/p&gt;
&lt;p&gt;Now it may jump to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You test again.&lt;/p&gt;
&lt;p&gt;Suppose:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c8&lt;/code&gt; fails.&lt;/p&gt;
&lt;p&gt;Mark it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Search space shrinks again.&lt;/p&gt;
&lt;p&gt;After a few iterations:&lt;/p&gt;
&lt;p&gt;Git identifies the exact commit responsible.&lt;/p&gt;
&lt;p&gt;You never manually walked through the entire history.&lt;/p&gt;
&lt;h2&gt;Don’t Forget to Reset Afterwards&lt;/h2&gt;
&lt;p&gt;This is easy to forget.&lt;/p&gt;
&lt;p&gt;During a bisect session, Git repeatedly checks out temporary commits.&lt;/p&gt;
&lt;p&gt;Once you’re finished, return to your original branch state:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without reset, you may wonder why your repository suddenly sits on some detached historical commit.&lt;/p&gt;
&lt;p&gt;Always reset when you&apos;re done.&lt;/p&gt;
&lt;h2&gt;Automating Git Bisect with Tests&lt;/h2&gt;
&lt;p&gt;Manual testing works.&lt;/p&gt;
&lt;p&gt;Automated testing works even better.&lt;/p&gt;
&lt;p&gt;If you already have a command that verifies correctness and returns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0 → success
non-zero → failure
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can automate the entire search.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect start
git bisect bad HEAD
git bisect good v1.2.0
git bisect run npm test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Git handles everything.&lt;/p&gt;
&lt;p&gt;For each candidate commit it will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;checkout commit&lt;/li&gt;
&lt;li&gt;run tests&lt;/li&gt;
&lt;li&gt;interpret result&lt;/li&gt;
&lt;li&gt;continue searching&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No manual interaction required.&lt;/p&gt;
&lt;p&gt;This becomes incredibly powerful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;failing unit tests&lt;/li&gt;
&lt;li&gt;broken builds&lt;/li&gt;
&lt;li&gt;regression bugs&lt;/li&gt;
&lt;li&gt;CI debugging&lt;/li&gt;
&lt;li&gt;flaky integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using Custom Validation Scripts&lt;/h2&gt;
&lt;p&gt;You aren&apos;t limited to npm test.&lt;/p&gt;
&lt;p&gt;Any script can drive bisect.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect run ./check-build.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect run pnpm lint
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or a custom Node validation script:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git bisect run node verify-api.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As long as the command returns a proper exit code, Git can automate the search.&lt;/p&gt;
&lt;p&gt;That flexibility makes &lt;code&gt;git bisect&lt;/code&gt; useful far beyond traditional testing.&lt;/p&gt;
&lt;h2&gt;Why Developers Forget This Tool Exists&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is oddly underused.&lt;/p&gt;
&lt;p&gt;Many developers know it exists but rarely reach for it.&lt;/p&gt;
&lt;p&gt;Instead they default to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reading commit history manually&lt;/li&gt;
&lt;li&gt;guessing likely changes&lt;/li&gt;
&lt;li&gt;debugging recent merges&lt;/li&gt;
&lt;li&gt;blaming dependencies&lt;/li&gt;
&lt;li&gt;reopening old PRs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes that works.&lt;/p&gt;
&lt;p&gt;Often it wastes time.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; gives you something much stronger:&lt;/p&gt;
&lt;p&gt;a systematic search strategy.&lt;/p&gt;
&lt;p&gt;No guessing.&lt;/p&gt;
&lt;p&gt;No intuition games.&lt;/p&gt;
&lt;p&gt;Just elimination.&lt;/p&gt;
&lt;h2&gt;When Git Bisect Becomes Especially Valuable&lt;/h2&gt;
&lt;p&gt;This tool shines in situations like:&lt;/p&gt;
&lt;h3&gt;Regression Bugs&lt;/h3&gt;
&lt;p&gt;Something used to work.&lt;/p&gt;
&lt;p&gt;Now it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;Classic bisect territory.&lt;/p&gt;
&lt;h3&gt;Large Teams&lt;/h3&gt;
&lt;p&gt;Many developers.&lt;/p&gt;
&lt;p&gt;Many commits.&lt;/p&gt;
&lt;p&gt;Unclear ownership.&lt;/p&gt;
&lt;p&gt;Bisect dramatically reduces noise.&lt;/p&gt;
&lt;h3&gt;Old Codebases&lt;/h3&gt;
&lt;p&gt;You don&apos;t remember when the behavior changed.&lt;/p&gt;
&lt;p&gt;The repository history does.&lt;/p&gt;
&lt;h3&gt;CI Failures&lt;/h3&gt;
&lt;p&gt;A build started failing somewhere in the last two weeks.&lt;/p&gt;
&lt;p&gt;Nobody knows where.&lt;/p&gt;
&lt;p&gt;Bisect knows.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is essentially binary search applied to commit history.&lt;/p&gt;
&lt;p&gt;That simple idea makes it incredibly effective.&lt;/p&gt;
&lt;p&gt;Instead of checking commits one after another, Git repeatedly cuts the search range in half.&lt;/p&gt;
&lt;p&gt;Forty commits?&lt;…</content:encoded></item><item><title>TypeScript Security From Backend to Browser</title><link>https://jsdev.space/typescript-security-guide/</link><guid isPermaLink="true">https://jsdev.space/typescript-security-guide/</guid><description>A deep practical guide to securing TypeScript applications across Node.js, React, Next.js, APIs, databases, SSR, and modern web infrastructure.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;TypeScript Is Not a Security Boundary&lt;/h2&gt;
&lt;p&gt;TypeScript has quietly become the default language of modern JavaScript engineering.&lt;/p&gt;
&lt;p&gt;React applications.&lt;/p&gt;
&lt;p&gt;Next.js platforms.&lt;/p&gt;
&lt;p&gt;NestJS APIs.&lt;/p&gt;
&lt;p&gt;GraphQL services.&lt;/p&gt;
&lt;p&gt;CLI tooling.&lt;/p&gt;
&lt;p&gt;Infrastructure scripts.&lt;/p&gt;
&lt;p&gt;Entire SaaS companies run on TypeScript stacks.&lt;/p&gt;
&lt;p&gt;And somewhere along the way, many developers start developing a subtle — and understandable — misconception:&lt;/p&gt;
&lt;p&gt;“We use TypeScript, so a large chunk of security problems are probably handled already.”&lt;/p&gt;
&lt;p&gt;Not explicitly.&lt;/p&gt;
&lt;p&gt;Not consciously.&lt;/p&gt;
&lt;p&gt;But indirectly.&lt;/p&gt;
&lt;p&gt;Strong types feel safe.&lt;/p&gt;
&lt;p&gt;Strict mode feels safe.&lt;/p&gt;
&lt;p&gt;Interfaces feel safe.&lt;/p&gt;
&lt;p&gt;DTOs, generics, discriminated unions, branded types — they create an environment that looks disciplined.&lt;/p&gt;
&lt;p&gt;And that discipline genuinely reduces bugs.&lt;/p&gt;
&lt;p&gt;But security vulnerabilities live in a different universe.&lt;/p&gt;
&lt;p&gt;A SQL injection payload is still a valid string.&lt;/p&gt;
&lt;p&gt;An XSS payload is still a valid string.&lt;/p&gt;
&lt;p&gt;A malicious JWT is still a valid string.&lt;/p&gt;
&lt;p&gt;A poisoned npm dependency still compiles perfectly.&lt;/p&gt;
&lt;p&gt;The TypeScript compiler is not failing.&lt;/p&gt;
&lt;p&gt;It is doing exactly what it was designed to do.&lt;/p&gt;
&lt;p&gt;This distinction matters.&lt;/p&gt;
&lt;p&gt;Because many production incidents happen precisely at the border where developers confuse type safety with security guarantees.&lt;/p&gt;
&lt;p&gt;In this article, we&apos;ll walk through the major security concerns affecting modern TypeScript applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;backend attacks&lt;/li&gt;
&lt;li&gt;database injections&lt;/li&gt;
&lt;li&gt;SSR vulnerabilities&lt;/li&gt;
&lt;li&gt;React and browser security&lt;/li&gt;
&lt;li&gt;JWT mistakes&lt;/li&gt;
&lt;li&gt;prototype pollution&lt;/li&gt;
&lt;li&gt;GraphQL abuse&lt;/li&gt;
&lt;li&gt;SSRF&lt;/li&gt;
&lt;li&gt;supply-chain attacks&lt;/li&gt;
&lt;li&gt;runtime validation&lt;/li&gt;
&lt;li&gt;modern tooling strategies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And most importantly:&lt;/p&gt;
&lt;p&gt;how to build a security model that works with TypeScript instead of expecting TypeScript to do security&apos;s job.&lt;/p&gt;
&lt;h2&gt;Backend Reality: HTTP Requests Arrive Before Type&lt;/h2&gt;
&lt;p&gt;TypeScript creates contracts between layers.&lt;/p&gt;
&lt;p&gt;Controllers expect DTOs.&lt;/p&gt;
&lt;p&gt;Services expect interfaces.&lt;/p&gt;
&lt;p&gt;Repositories expect known shapes.&lt;/p&gt;
&lt;p&gt;The internet ignores all of them.&lt;/p&gt;
&lt;p&gt;Your backend does not receive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type LoginRequest = {
  email: string;
  password: string;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It receives:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;raw bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those bytes might become JSON.&lt;/p&gt;
&lt;p&gt;Or malformed JSON.&lt;/p&gt;
&lt;p&gt;Or intentionally malicious JSON.&lt;/p&gt;
&lt;p&gt;Or oversized payloads.&lt;/p&gt;
&lt;p&gt;Or nested objects designed to break assumptions.&lt;/p&gt;
&lt;p&gt;Before your beautiful TypeScript types even exist, untrusted data already crossed your trust boundary.&lt;/p&gt;
&lt;p&gt;That is why runtime validation matters.&lt;/p&gt;
&lt;p&gt;Not optional validation.&lt;/p&gt;
&lt;p&gt;Not “we validate most routes.”&lt;/p&gt;
&lt;p&gt;Boundary validation.&lt;/p&gt;
&lt;p&gt;Every request.&lt;/p&gt;
&lt;p&gt;Every external payload.&lt;/p&gt;
&lt;p&gt;Every webhook.&lt;/p&gt;
&lt;p&gt;Every environment variable.&lt;/p&gt;
&lt;p&gt;Every file import.&lt;/p&gt;
&lt;p&gt;Without this mindset, types become documentation — not protection.&lt;/p&gt;
&lt;h2&gt;SQL Injection Still Exists Inside ORM Code&lt;/h2&gt;
&lt;p&gt;Many developers associate SQL injection with early PHP tutorials.&lt;/p&gt;
&lt;p&gt;String concatenation.&lt;/p&gt;
&lt;p&gt;Handwritten SQL.&lt;/p&gt;
&lt;p&gt;Obvious mistakes.&lt;/p&gt;
&lt;p&gt;Modern ORMs supposedly solved that problem.&lt;/p&gt;
&lt;p&gt;Mostly true.&lt;/p&gt;
&lt;p&gt;Mostly.&lt;/p&gt;
&lt;p&gt;Because ORMs reduce risk until developers partially bypass them.&lt;/p&gt;
&lt;p&gt;Consider this seemingly harmless endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&quot;/users&quot;, async (req, res) =&amp;gt; {
  const { sortColumn, order } = req.query;

  const users = await connection.query(
    `SELECT * FROM users
     ORDER BY ${sortColumn} ${order}`
  );

  res.json(users);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From TypeScript&apos;s perspective:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sortColumn: string
order: string
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything typechecks.&lt;/p&gt;
&lt;p&gt;The database sees something different.&lt;/p&gt;
&lt;p&gt;Suppose an attacker sends:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/users?sortColumn=name&amp;amp;order=ASC;DROP TABLE users;--
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The database does not care that your IDE showed no errors.&lt;/p&gt;
&lt;p&gt;The query parser now receives executable SQL.&lt;/p&gt;
&lt;p&gt;This is where many teams misunderstand what ORMs actually guarantee.&lt;/p&gt;
&lt;p&gt;An ORM reduces accidental SQL construction.&lt;/p&gt;
&lt;p&gt;It does not automatically sanitize arbitrary string interpolation.&lt;/p&gt;
&lt;p&gt;Safer approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allowedColumns = [
  &quot;name&quot;,
  &quot;email&quot;,
  &quot;createdAt&quot;
] as const;

const allowedDirections = [
  &quot;ASC&quot;,
  &quot;DESC&quot;
] as const;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Validate.&lt;/p&gt;
&lt;p&gt;Allowlist.&lt;/p&gt;
&lt;p&gt;Then build controlled queries.&lt;/p&gt;
&lt;p&gt;Or better:&lt;/p&gt;
&lt;p&gt;use query APIs that parameterize values automatically.&lt;/p&gt;
&lt;h2&gt;The Hidden ORM Trap: &lt;code&gt;QueryBuilder&lt;/code&gt; and Raw()&lt;/h2&gt;
&lt;p&gt;This is where real-world codebases become interesting.&lt;/p&gt;
&lt;p&gt;Developers often say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“We already use QueryBuilder.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Good.&lt;/p&gt;
&lt;p&gt;That alone changes nothing.&lt;/p&gt;
&lt;p&gt;Unsafe &lt;code&gt;QueryBuilder&lt;/code&gt; code still exists everywhere.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const qb = userRepository.createQueryBuilder(&quot;user&quot;);

qb.where(
  `user.role=&apos;${filter}&apos;`
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks sophisticated.&lt;/p&gt;
&lt;p&gt;Still injection-prone.&lt;/p&gt;
&lt;p&gt;Same vulnerability.&lt;/p&gt;
&lt;p&gt;New abstraction layer.&lt;/p&gt;
&lt;p&gt;TypeORM&apos;s &lt;code&gt;Raw()&lt;/code&gt; helper is another common footgun.&lt;/p&gt;
&lt;p&gt;Unsafe:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;where: {
  name: Raw(
    alias =&amp;gt; `${alias} LIKE &apos;%${query}%&apos;`
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, user input becomes part of executable SQL again.&lt;/p&gt;
&lt;p&gt;Safe alternative:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;where: {
  name: Raw(
    alias =&amp;gt; `${alias} ILIKE :query`,
    {
      query: `%${query}%`
    }
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parameterized placeholders exist for a reason.&lt;/p&gt;
&lt;p&gt;Use them.&lt;/p&gt;
&lt;h2&gt;NoSQL Injection Is Still Injection&lt;/h2&gt;
&lt;p&gt;MongoDB developers sometimes assume they escaped SQL problems entirely.&lt;/p&gt;
&lt;p&gt;Not quite.&lt;/p&gt;
&lt;p&gt;You traded syntax.&lt;/p&gt;
&lt;p&gt;Not threat models.&lt;/p&gt;
&lt;p&gt;Classic vulnerable login handler:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.post(&quot;/login&quot;, async (req, res) =&amp;gt; {
    const { username,password } = req.body;
    const user =
      await User.findOne({
        username,
        password
      });
    if (user) {
      return res.sendStatus(200);
    }
    res.sendStatus(401);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine this payload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;username&quot;: {
    &quot;$ne&quot;: null
  },
  &quot;password&quot;: {
    &quot;$ne&quot;: null
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting query becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  username: {
    $ne: null
  },

  password: {
    $ne: null
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congratulations.&lt;/p&gt;
&lt;p&gt;You may have just authenticated the first matching user.&lt;/p&gt;
&lt;p&gt;The compiler did not fail.&lt;/p&gt;
&lt;p&gt;Because objects are valid objects.&lt;/p&gt;
&lt;p&gt;Runtime validation solves this.&lt;/p&gt;
&lt;p&gt;Not interfaces.&lt;/p&gt;
&lt;p&gt;Runtime.&lt;/p&gt;
&lt;p&gt;Zod example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const LoginSchema = z.object({
  username: z.string(),
  password: z.string(),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only parsed data enters business logic.&lt;/p&gt;
&lt;p&gt;Everything else dies at the boundary.&lt;/p&gt;
&lt;h2&gt;Runtime Validation Is Not Optional Infrastructure&lt;/h2&gt;
&lt;p&gt;This deserves its own section.&lt;/p&gt;
&lt;p&gt;Many TypeScript codebases validate inside handlers.&lt;/p&gt;
&lt;p&gt;After logic begins.&lt;/p&gt;
&lt;p&gt;After database calls.&lt;/p&gt;
&lt;p&gt;After assumptions already spread through the request pipeline.&lt;/p&gt;
&lt;p&gt;Validation belongs at the trust boundary.&lt;/p&gt;
&lt;p&gt;Modern stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zod&lt;/li&gt;
&lt;li&gt;class-validator&lt;/li&gt;
&lt;li&gt;Valibot&lt;/li&gt;
&lt;li&gt;io-ts&lt;/li&gt;
&lt;li&gt;custom parsers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specific library matters less than the architectural rule:&lt;/p&gt;
&lt;p&gt;external data is untrusted until runtime validation succeeds.&lt;/p&gt;
&lt;p&gt;This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API requests&lt;/li&gt;
&lt;li&gt;webhook payloads&lt;/li&gt;
&lt;li&gt;Kafka events&lt;/li&gt;
&lt;li&gt;Redis messages&lt;/li&gt;
&lt;li&gt;JSON imports&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;browser localStorage&lt;/li&gt;
&lt;li&gt;server actions&lt;/li&gt;
&lt;li&gt;SSR hydration data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TypeScript cannot validate any of those at runtime.&lt;/p&gt;
&lt;p&gt;That is not a limitation.&lt;/p&gt;
&lt;p&gt;That is literally outside its job description.&lt;/p…</content:encoded></item><item><title>Howto Catch Risky JavaScript Bugs with eslint-plugin-security</title><link>https://jsdev.space/howto/eslint-plugin-security/</link><guid isPermaLink="true">https://jsdev.space/howto/eslint-plugin-security/</guid><description>Learn how to use eslint-plugin-security to detect dangerous JavaScript patterns, unsafe Node.js code, dynamic execution, and hidden security mistakes.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most developers already use ESLint.&lt;/p&gt;
&lt;p&gt;You probably have rules for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unused variables&lt;/li&gt;
&lt;li&gt;import sorting&lt;/li&gt;
&lt;li&gt;React hooks&lt;/li&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;TypeScript correctness&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But many projects still miss an entirely different category of bugs:&lt;/p&gt;
&lt;p&gt;security mistakes hiding inside valid JavaScript.&lt;/p&gt;
&lt;p&gt;The code compiles.&lt;/p&gt;
&lt;p&gt;Tests pass.&lt;/p&gt;
&lt;p&gt;TypeScript is happy.&lt;/p&gt;
&lt;p&gt;Production deploys successfully.&lt;/p&gt;
&lt;p&gt;Then somebody discovers an unsafe regex freezing your API server, a dynamic &lt;code&gt;require()&lt;/code&gt; loading unexpected modules, or a command injection vulnerability hiding inside a small utility function nobody questioned during review.&lt;/p&gt;
&lt;p&gt;That is exactly where &lt;a href=&quot;https://github.com/eslint-community/eslint-plugin-security&quot;&gt;eslint-plugin-security&lt;/a&gt; becomes useful.&lt;/p&gt;
&lt;p&gt;It does not replace penetration testing.&lt;/p&gt;
&lt;p&gt;It does not magically secure your application.&lt;/p&gt;
&lt;p&gt;But it can catch dangerous patterns early — directly inside your editor and CI pipeline.&lt;/p&gt;
&lt;p&gt;In this article we&apos;ll look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what eslint-plugin-security actually does&lt;/li&gt;
&lt;li&gt;how to install it&lt;/li&gt;
&lt;li&gt;how to configure ESLint v9 Flat Config&lt;/li&gt;
&lt;li&gt;real security problems it can detect&lt;/li&gt;
&lt;li&gt;common false positives&lt;/li&gt;
&lt;li&gt;how to use it in production projects&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why Normal ESLint Rules Are Not Enough&lt;/h2&gt;
&lt;p&gt;Standard ESLint focuses on code quality.&lt;/p&gt;
&lt;p&gt;It helps prevent problems like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const username = &quot;Alex&quot;;

console.log(userName);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = await fetchProfile()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;without handling the Promise properly.&lt;/p&gt;
&lt;p&gt;Useful?&lt;/p&gt;
&lt;p&gt;Absolutely.&lt;/p&gt;
&lt;p&gt;Security-focused?&lt;/p&gt;
&lt;p&gt;Not really.&lt;/p&gt;
&lt;p&gt;Security bugs usually look perfectly legal to JavaScript.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const query = req.query.script;

eval(query);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ESLint sees valid syntax.&lt;/p&gt;
&lt;p&gt;JavaScript sees valid syntax.&lt;/p&gt;
&lt;p&gt;Your attacker sees opportunity.&lt;/p&gt;
&lt;p&gt;Security linting exists because many dangerous patterns are still syntactically correct code.&lt;/p&gt;
&lt;h2&gt;Installing eslint-plugin-security&lt;/h2&gt;
&lt;p&gt;Installation is straightforward.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -D eslint-plugin-security
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;ESLint v9 Flat Config Setup&lt;/h2&gt;
&lt;p&gt;Modern ESLint projects typically use Flat Config.&lt;/p&gt;
&lt;p&gt;Create or update:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eslint.config.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Configuration example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import security from &quot;eslint-plugin-security&quot;;

export default [
  {
    plugins: {
      security,
    },

    rules: {
      ...security.configs.recommended.rules,
    },
  },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is enough to enable the recommended ruleset.&lt;/p&gt;
&lt;p&gt;You can now run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx eslint . --ext .js,.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Legacy .eslintrc Configuration&lt;/h2&gt;
&lt;p&gt;Older projects may still use .eslintrc.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;plugins&quot;: [&quot;security&quot;],
  &quot;extends&quot;: [&quot;plugin:security/recommended&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple.&lt;/p&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #1 — Dynamic &lt;code&gt;eval()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let&apos;s start with the obvious one.&lt;/p&gt;
&lt;p&gt;JavaScript&apos;s infamous &lt;code&gt;eval()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Bad example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const sourceCode = req.body.expression;

const output = eval(sourceCode);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If external input reaches this function, your application can execute arbitrary JavaScript.&lt;/p&gt;
&lt;p&gt;That is rarely what you want.&lt;/p&gt;
&lt;p&gt;The plugin flags patterns like this immediately.&lt;/p&gt;
&lt;p&gt;Safer alternatives often exist.&lt;/p&gt;
&lt;p&gt;Instead of dynamic execution:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eval(&quot;5 + 10&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;prefer explicit logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const operations = {
  add: (x: number, y: number) =&amp;gt; x + y,
  subtract: (x: number, y: number) =&amp;gt; x - y,
};

const result = operations.add(5, 10);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Less magical.&lt;/p&gt;
&lt;p&gt;Much safer.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #2 — Non-Literal require()&lt;/h2&gt;
&lt;p&gt;This rule surprises many developers.&lt;/p&gt;
&lt;p&gt;Consider:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const packageName = req.query.library;

const dependency = require(packageName);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks flexible.&lt;/p&gt;
&lt;p&gt;Also potentially dangerous.&lt;/p&gt;
&lt;p&gt;Because attackers might control what module gets loaded.&lt;/p&gt;
&lt;p&gt;Security plugins dislike dynamic imports for good reason.&lt;/p&gt;
&lt;p&gt;Prefer explicit allowlists.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allowedLibraries = {
  csv: require(&quot;./parsers/csv&quot;),
  json: require(&quot;./parsers/json&quot;),
};

const parser = allowedLibraries[userFormat];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the input selects from known modules instead of arbitrary paths.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #3 — Unsafe Child Process Execution&lt;/h2&gt;
&lt;p&gt;Node.js applications often interact with shell commands.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { exec } from &quot;node:child_process&quot;;

exec(`ping ${hostname}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks innocent.&lt;/p&gt;
&lt;p&gt;Until somebody submits:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;localhost &amp;amp;&amp;amp; rm -rf /&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Welcome to command injection.&lt;/p&gt;
&lt;p&gt;This is one of the oldest backend vulnerabilities.&lt;/p&gt;
&lt;p&gt;Safer approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { execFile } from &quot;node:child_process&quot;;

execFile(
  &quot;ping&quot;,
  [hostname],
  (error, stdout) =&amp;gt; {
    console.log(stdout);
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Arguments stay separated from the command itself.&lt;/p&gt;
&lt;p&gt;Much safer.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #4 — Object Injection&lt;/h2&gt;
&lt;p&gt;One of the more controversial rules.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const fieldName = req.query.sort;

database[fieldName] = value;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why does the plugin care?&lt;/p&gt;
&lt;p&gt;Because user-controlled property access can sometimes lead to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prototype pollution&lt;/li&gt;
&lt;li&gt;unsafe mutation&lt;/li&gt;
&lt;li&gt;unexpected behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;payload[&quot;__proto__&quot;] = {
  compromised: true,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on the environment and codebase, this can become dangerous.&lt;/p&gt;
&lt;p&gt;Safer approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allowedFields = [
  &quot;email&quot;,
  &quot;username&quot;,
  &quot;createdAt&quot;,
];

if (allowedFields.includes(fieldName)) {
  database[fieldName] = value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explicit validation dramatically reduces risk.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #5 — Unsafe Regular Expressions&lt;/h2&gt;
&lt;p&gt;Regex performance issues are underrated.&lt;/p&gt;
&lt;p&gt;A poorly designed regex can lock an application thread.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const pattern =
  /(a+)+$/;

pattern.test(userInput);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certain inputs can trigger catastrophic backtracking.&lt;/p&gt;
&lt;p&gt;Your server CPU spikes.&lt;/p&gt;
&lt;p&gt;Requests hang.&lt;/p&gt;
&lt;p&gt;Users complain.&lt;/p&gt;
&lt;p&gt;This category is often called ReDoS — Regular Expression Denial of Service.&lt;/p&gt;
&lt;p&gt;The security plugin warns about suspicious constructions.&lt;/p&gt;
&lt;p&gt;Even better: test regex performance intentionally.&lt;/p&gt;
&lt;p&gt;Example safer pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const emailMatcher =
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple patterns usually behave more predictably.&lt;/p&gt;
&lt;h2&gt;Dangerous Pattern #6 — Timing Attack Risks&lt;/h2&gt;
&lt;p&gt;Some rules target insecure comparisons.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (
  suppliedSecret === process.env.API_SECRET
) {
  authenticate();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks harmless.&lt;/p&gt;
&lt;p&gt;But direct string comparison may leak timing information.&lt;/p&gt;
&lt;p&gt;In sensitive environments, use dedicated cryptographic helpers.&lt;/p&gt;
&lt;p&gt;Node.js provides:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import crypto from &quot;node:crypto&quot;;

const verified =
  crypto.timingSafeEqual(
    Buffer.from(inputSecret),
    Buffer.from(secretValue)
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do most applications need this?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;Do authentication systems care?&lt;/p&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;h2&gt;False Positives: When the Plugin Gets Noisy&lt;/h2&gt;
&lt;p&gt;This is important.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eslint-plugin-security&lt;/code&gt; is not perfect.&lt;/p&gt;
&lt;p&gt;Some rules intentionally behave aggressively.&lt;/p&gt;
&lt;p&gt;You may see warnings like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config[key] = value;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;even when your code is completely safe.&lt;/p&gt;
&lt;p&gt;That does not automatically mean the plugin is wrong.&lt;/p&gt;
&lt;p&gt;Security tooling often prefers better safe than sorry.&lt;/p&gt;
&lt;p&gt;Still, developers need practical workflows.&lt;/p&gt;
&lt;p&gt;You have several opt…</content:encoded></item><item><title>5 Docker Compose Settings Missing From Most Production Setups</title><link>https://jsdev.space/docker-compose-production-settings/</link><guid isPermaLink="true">https://jsdev.space/docker-compose-production-settings/</guid><description>Your docker-compose.yml may work locally, but production exposes missing limits, restart policies, log rotation, healthchecks, and backup gaps.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You wrote a &lt;code&gt;docker-compose.yml&lt;/code&gt;, started everything locally, and it worked perfectly.&lt;/p&gt;
&lt;p&gt;You deployed it to a server.&lt;/p&gt;
&lt;p&gt;Then you ran:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything looked fine for a week.&lt;/p&gt;
&lt;p&gt;Then Postgres quietly consumed all available memory, Linux triggered the OOM killer, and your application died instead of the database.&lt;/p&gt;
&lt;p&gt;Or your service crashed at 2 AM and stayed offline because Docker&apos;s default restart policy is effectively:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;restart: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or container logs silently grew to 40 GB and filled the disk.&lt;/p&gt;
&lt;p&gt;None of these problems are exotic. They are common production failures caused by Compose files that were written for local development and then promoted to servers without production guardrails.&lt;/p&gt;
&lt;p&gt;The uncomfortable part is that most of these failures are fixed with a few lines of configuration.&lt;/p&gt;
&lt;p&gt;The problem is that local development rarely forces you to care. Your laptop has enough RAM, plenty of disk space, and you are usually sitting next to the terminal when something breaks. A production server is different. It runs unattended. It accumulates logs. It handles traffic spikes. It restarts after failures. It stores data that should not disappear because someone used the wrong flag.&lt;/p&gt;
&lt;p&gt;This article covers five Docker Compose settings that are easy to forget but painful to miss.&lt;/p&gt;
&lt;h2&gt;1. Set Memory and CPU Limits&lt;/h2&gt;
&lt;p&gt;By default, a container can use as much CPU and memory as the host allows.&lt;/p&gt;
&lt;p&gt;That sounds convenient locally. In production, it is a risk.&lt;/p&gt;
&lt;p&gt;Imagine a small server running three services:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your application&lt;/li&gt;
&lt;li&gt;Postgres&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without limits, any of them can consume enough resources to destabilize the whole machine. In many real systems, the database is the first service to grow aggressively. Postgres sees available memory and uses it. Redis may do the same if its dataset grows. Your application may spike during traffic bursts, image processing, queue jobs, or bad queries.&lt;/p&gt;
&lt;p&gt;When the host runs out of memory, Linux does not politely ask Docker which container should stop. The OOM killer chooses a process. Sometimes it kills the database. Sometimes it kills your application while it is handling hundreds of requests.&lt;/p&gt;
&lt;p&gt;Add explicit resource boundaries:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: &quot;1.0&quot;
        reservations:
          memory: 256M

  postgres:
    image: postgres:16-alpine
    deploy:
      resources:
        limits:
          memory: 1G
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;limits&lt;/code&gt; are the hard ceiling. If the container exceeds the memory limit, Docker isolates the failure to that container instead of letting it consume the entire host.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;reservations&lt;/code&gt; describe the amount of resources the service expects to have available. They are useful when Compose is used with orchestration features, but the most important part for a simple server is still the hard memory limit.&lt;/p&gt;
&lt;p&gt;You can check whether a container was killed because of memory pressure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker inspect myapp --format=&apos;{{.State.OOMKilled}}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the result is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then the container was killed after exceeding its memory allowance.&lt;/p&gt;
&lt;p&gt;For Postgres, memory limits should also influence database configuration. If you limit the container to &lt;code&gt;1G&lt;/code&gt;, setting &lt;code&gt;shared_buffers&lt;/code&gt; somewhere around &lt;code&gt;256MB&lt;/code&gt; is a reasonable starting point. The exact value depends on workload, but the important idea is simple: database tuning should match the container&apos;s actual memory budget, not the host&apos;s total memory.&lt;/p&gt;
&lt;h2&gt;2. Add a Restart Policy&lt;/h2&gt;
&lt;p&gt;Docker does not automatically restart failed containers unless you tell it to.&lt;/p&gt;
&lt;p&gt;That surprises many people.&lt;/p&gt;
&lt;p&gt;The default behavior is essentially:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;restart: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your application crashes, it stays down.&lt;/p&gt;
&lt;p&gt;That may be fine during development. It is not fine at 2 AM.&lt;/p&gt;
&lt;p&gt;For long-running services, use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;unless-stopped&lt;/code&gt; is usually the safest default for application services.&lt;/p&gt;
&lt;p&gt;It means Docker will restart the container after crashes, daemon restarts, and machine reboots. But if you intentionally stop the service, Docker respects that and does not immediately bring it back.&lt;/p&gt;
&lt;p&gt;That makes it more practical than &lt;code&gt;always&lt;/code&gt; for many small production setups. &lt;code&gt;always&lt;/code&gt; can be annoying during maintenance because Docker may restart containers you intentionally stopped.&lt;/p&gt;
&lt;p&gt;One-off jobs are different.&lt;/p&gt;
&lt;p&gt;Migrations, seed scripts, and maintenance tasks should not restart forever:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  migrator:
    image: myapp:latest
    command: [&quot;python&quot;, &quot;manage.py&quot;, &quot;migrate&quot;]
    restart: &quot;no&quot;

  app:
    image: myapp:latest
    restart: unless-stopped
    depends_on:
      migrator:
        condition: service_completed_successfully
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern avoids a common production mistake: starting the application before the schema is ready.&lt;/p&gt;
&lt;p&gt;If the migration fails, the application should not boot and pretend everything is fine. It should fail early, loudly, and predictably.&lt;/p&gt;
&lt;h2&gt;3. Rotate Container Logs&lt;/h2&gt;
&lt;p&gt;Docker&apos;s default &lt;code&gt;json-file&lt;/code&gt; logging driver writes logs to disk.&lt;/p&gt;
&lt;p&gt;If you do not configure rotation, those files can grow without a practical limit.&lt;/p&gt;
&lt;p&gt;A service logging every request at 100 requests per second can generate a surprising amount of data. Over weeks or months, logs can quietly consume tens of gigabytes.&lt;/p&gt;
&lt;p&gt;When the disk fills up, the failure is ugly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;containers may stop&lt;/li&gt;
&lt;li&gt;writes may fail&lt;/li&gt;
&lt;li&gt;Docker itself may become unstable&lt;/li&gt;
&lt;li&gt;recovery may require manual cleanup on the host&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add log rotation directly in Compose:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    logging:
      driver: json-file
      options:
        max-size: &quot;10m&quot;
        max-file: &quot;3&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means each log file can grow to 10 MB, and Docker keeps up to three files per container.&lt;/p&gt;
&lt;p&gt;So the container uses roughly 30 MB for logs before old files are rotated out.&lt;/p&gt;
&lt;p&gt;For many services, that is enough for quick diagnostics. If you need more history on the host, increase the limits:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logging:
  driver: json-file
  options:
    max-size: &quot;50m&quot;
    max-file: &quot;10&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gives you up to 500 MB per container.&lt;/p&gt;
&lt;p&gt;You can also define global defaults in &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;log-driver&quot;: &quot;json-file&quot;,
  &quot;log-opts&quot;: {
    &quot;max-size&quot;: &quot;10m&quot;,
    &quot;max-file&quot;: &quot;5&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After changing daemon settings, restart Docker so new containers use the configuration.&lt;/p&gt;
&lt;p&gt;To inspect current log usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;du -sh /var/lib/docker/containers/*/*-json.log | sort -h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see multi-gigabyte JSON log files, log rotation was missed.&lt;/p&gt;
&lt;p&gt;That is not a logging strategy. It is a delayed outage.&lt;/p&gt;
&lt;h2&gt;4. Add Healthchecks&lt;/h2&gt;
&lt;p&gt;Docker knows whether a process is running.&lt;/p&gt;
&lt;p&gt;It does not automatically know whether your application is useful.&lt;/p&gt;
&lt;p&gt;A container can be in a &lt;code&gt;running&lt;/code&gt; state while the actual service is broken. The process may exist, but the app may be stuck, overloaded, disconnected from the database, or unable to respond to HTTP requests.&lt;/p&gt;
&lt;p&gt;Add a healthcheck:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    image: myapp:latest
    healthcheck:
      test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-f&quot;, &quot;http://localhost:8080/health&quot;]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;test&lt;/code&gt; command should check something meaningful.&lt;/p&gt;…</content:encoded></item><item><title>Meet Fate: Building an Astro Blog with a Modern React Data Client</title><link>https://jsdev.space/meet-fate-react-astro/</link><guid isPermaLink="true">https://jsdev.space/meet-fate-react-astro/</guid><description>Explore Fate through a practical Astro 6 blog project with React islands, MDX, Tailwind v4, search, dark mode, tags, and modern client-side data patterns.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React solved UI a long time ago.
Data, however, is still where many applications quietly accumulate complexity.&lt;/p&gt;
&lt;p&gt;Between &lt;code&gt;fetch()&lt;/code&gt;, &lt;code&gt;useEffect()&lt;/code&gt;, loading states, caching layers, duplicated requests, and client synchronization, modern React applications often end up inventing their own mini data framework.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fate.technology/&quot;&gt;Fate&lt;/a&gt; takes a different approach.&lt;/p&gt;
&lt;p&gt;It is a modern React data client built around composable data views, normalized caching, and structured client data access.&lt;/p&gt;
&lt;p&gt;Although Fate can power much larger systems — dashboards, realtime applications, complex authenticated interfaces — this article explores it in a smaller and perhaps less expected environment:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;an Astro 6 static blog&lt;/strong&gt; with React islands.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; is designed around static generation. Blogs are primarily content-driven. Many projects can be built using Astro alone, without React, without a client data layer, and often without any client-side JavaScript at all.&lt;/p&gt;
&lt;p&gt;That is true.&lt;/p&gt;
&lt;p&gt;But modern Astro applications frequently mix static rendering with selective interactivity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React islands&lt;/li&gt;
&lt;li&gt;dynamic search widgets&lt;/li&gt;
&lt;li&gt;authenticated UI sections&lt;/li&gt;
&lt;li&gt;personalized dashboards&lt;/li&gt;
&lt;li&gt;API-driven components&lt;/li&gt;
&lt;li&gt;client-side stateful interfaces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once React islands enter the picture, questions about data access eventually follow.&lt;/p&gt;
&lt;p&gt;Instead of building an abstract enterprise dashboard, we will explore Fate through something smaller, concrete, and easier to reason about.&lt;/p&gt;
&lt;p&gt;By the end of this article, we will build a modern Astro 6 blog featuring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MDX content collections&lt;/li&gt;
&lt;li&gt;dynamic blog routes&lt;/li&gt;
&lt;li&gt;React islands&lt;/li&gt;
&lt;li&gt;Tailwind v4 styling&lt;/li&gt;
&lt;li&gt;client-side search&lt;/li&gt;
&lt;li&gt;dark mode&lt;/li&gt;
&lt;li&gt;tag pages&lt;/li&gt;
&lt;li&gt;API routes&lt;/li&gt;
&lt;li&gt;a minimal working Fate integration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Along the way, we will also answer a more practical question:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do you actually need Fate for an Astro blog?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Usually, no.&lt;/p&gt;
&lt;p&gt;But understanding where it fits can tell us a lot about modern React data architecture.&lt;/p&gt;
&lt;h2&gt;What Is Fate?&lt;/h2&gt;
&lt;p&gt;Before touching Astro, it is worth understanding what Fate actually is.&lt;/p&gt;
&lt;p&gt;Fate is not a state manager.&lt;/p&gt;
&lt;p&gt;It is not another &lt;code&gt;useState()&lt;/code&gt; replacement.&lt;/p&gt;
&lt;p&gt;It is not React Query with slightly different naming.&lt;/p&gt;
&lt;p&gt;Instead, Fate positions itself as a modern React data client.&lt;/p&gt;
&lt;p&gt;Its API revolves around concepts like views, requests, transports, and structured client data access.&lt;/p&gt;
&lt;p&gt;One of the most recognizable pieces of the API is &lt;code&gt;view()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { view } from &quot;react-fate&quot;;

type Post = {
  id: string;
  title: string;
  description: string;
};

const PostView = view&amp;lt;Post&amp;gt;()({
  id: true,
  title: true,
  description: true,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of thinking primarily in terms of raw network requests, Fate encourages components to declare which pieces of data they actually care about.&lt;/p&gt;
&lt;p&gt;If this feels somewhat familiar to GraphQL fragments, Relay, or normalized client architectures, that is not accidental.&lt;/p&gt;
&lt;p&gt;The idea is not simply &lt;em&gt;&quot;fetch some JSON&quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The idea is structured client-side data composition.&lt;/p&gt;
&lt;p&gt;That distinction becomes much more interesting once applications begin growing beyond a few isolated components.&lt;/p&gt;
&lt;h2&gt;Why Use Fate in an Astro Project?&lt;/h2&gt;
&lt;p&gt;At this point, a reasonable question appears.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Astro? And why Fate inside Astro?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After all, Astro was built around a static-first philosophy.&lt;/p&gt;
&lt;p&gt;A typical Astro blog can happily exist without React, without client-side state, and without a dedicated data layer.&lt;/p&gt;
&lt;p&gt;In many cases, that is exactly what makes Astro attractive.&lt;/p&gt;
&lt;p&gt;You write content.&lt;/p&gt;
&lt;p&gt;Astro generates HTML.&lt;/p&gt;
&lt;p&gt;The browser receives mostly static pages.&lt;/p&gt;
&lt;p&gt;Everything stays fast.&lt;/p&gt;
&lt;p&gt;A minimal blog can often be implemented using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Markdown or MDX content&lt;/li&gt;
&lt;li&gt;static routes&lt;/li&gt;
&lt;li&gt;server-side content collections&lt;/li&gt;
&lt;li&gt;zero client JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a large percentage of publishing websites, this is already enough.&lt;/p&gt;
&lt;p&gt;So introducing React — and especially a specialized React data client — might initially sound like unnecessary complexity.&lt;/p&gt;
&lt;p&gt;That concern is fair.&lt;/p&gt;
&lt;p&gt;However, modern Astro projects frequently extend beyond purely static pages.&lt;/p&gt;
&lt;p&gt;Consider a few examples.&lt;/p&gt;
&lt;p&gt;An otherwise static blog might eventually gain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client-side search&lt;/li&gt;
&lt;li&gt;bookmarks&lt;/li&gt;
&lt;li&gt;reading history&lt;/li&gt;
&lt;li&gt;personalized recommendations&lt;/li&gt;
&lt;li&gt;authenticated dashboards&lt;/li&gt;
&lt;li&gt;realtime counters&lt;/li&gt;
&lt;li&gt;notification panels&lt;/li&gt;
&lt;li&gt;interactive filtering systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where &lt;strong&gt;Astro islands architecture&lt;/strong&gt; becomes interesting.&lt;/p&gt;
&lt;p&gt;Astro allows us to selectively introduce interactivity only where it is needed.&lt;/p&gt;
&lt;p&gt;Instead of hydrating an entire application, we can hydrate isolated components.&lt;/p&gt;
&lt;p&gt;A search widget can become interactive.&lt;/p&gt;
&lt;p&gt;A theme toggle can manage client state.&lt;/p&gt;
&lt;p&gt;A live dashboard card can fetch dynamic data.&lt;/p&gt;
&lt;p&gt;Everything else remains static.&lt;/p&gt;
&lt;p&gt;That balance is one of Astro&apos;s strongest design choices.&lt;/p&gt;
&lt;p&gt;For example, a React search component can be hydrated independently:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;BlogSearch
  posts={searchPosts}
  client:load
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only this island becomes interactive.&lt;/p&gt;
&lt;p&gt;The rest of the page remains static HTML.&lt;/p&gt;
&lt;p&gt;This approach gives us a useful playground for exploring Fate.&lt;/p&gt;
&lt;p&gt;We do not need a massive enterprise application to understand a data client.&lt;/p&gt;
&lt;p&gt;We simply need a project where static content and client-side interactivity coexist.&lt;/p&gt;
&lt;p&gt;An Astro blog happens to be a surprisingly practical environment for exactly that.&lt;/p&gt;
&lt;h2&gt;Creating the Project&lt;/h2&gt;
&lt;p&gt;We will begin with a minimal Astro 6 setup.&lt;/p&gt;
&lt;p&gt;Create a new project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create astro@latest fate-blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Choose the following options during initialization:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Template: Empty
TypeScript: Yes
Install dependencies: Yes
Initialize Git: Yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into the project directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd fate-blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the pieces we will use throughout the article.&lt;/p&gt;
&lt;p&gt;React integration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx astro add react
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MDX support:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx astro add mdx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tailwind v4:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx astro add tailwind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, install Fate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install react-fate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our dependency list will look roughly like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;dependencies&quot;: {
  &quot;@astrojs/mdx&quot;: &quot;^5.0.6&quot;,
  &quot;@astrojs/react&quot;: &quot;^5.0.5&quot;,
  &quot;@tailwindcss/vite&quot;: &quot;^4.3.0&quot;,
  &quot;@types/react&quot;: &quot;^19.2.15&quot;,
  &quot;@types/react-dom&quot;: &quot;^19.2.3&quot;,
  &quot;astro&quot;: &quot;^6.3.6&quot;,
  &quot;react&quot;: &quot;^19.2.6&quot;,
  &quot;react-dom&quot;: &quot;^19.2.6&quot;,
  &quot;react-fate&quot;: &quot;^1.0.3&quot;,
  &quot;tailwindcss&quot;: &quot;^4.3.0&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuring Content Collections in Astro 6&lt;/h3&gt;
&lt;p&gt;Astro 6 uses the newer content loader approach.&lt;/p&gt;
&lt;p&gt;Create:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/content.config.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { defineCollection, z } from &quot;astro:content&quot;;
import { glob } from &quot;astro/loaders&quot;;

const posts = defineCollection({
  loader: glob({
    pattern: &quot;**/*.{md,mdx}&quot;,
    base: &quot;./src/content/posts&quot;,
  }),

  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.coerce.date(),
    category: z.string(),
    tags: z.array(z.string()).default([]),
  }),
});

export const collections = {
  posts,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration defines a typed content collection named &lt;code&gt;posts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every post must now provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;description&lt;/li&gt;
&lt;li&gt;publication date&lt;/li&gt;
&lt;li&gt;cat…</content:encoded></item><item><title>Friday Links #38 — JavaScript Trends, AI Dev Tools &amp; Releases</title><link>https://jsdev.space/friday/friday-38/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-38/</guid><description>A curated roundup of JavaScript news, AI developer tools, framework releases, browser updates, and standout projects from the modern web ecosystem.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-38.png&quot; alt=&quot;Friday Links #38&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another busy week in the JavaScript world.&lt;/p&gt;
&lt;p&gt;Framework releases kept shipping, browser vendors continued experimenting, AI development tools evolved at an uncomfortable speed, and open-source maintainers somehow still found time to launch new libraries, runtimes, and developer utilities.&lt;/p&gt;
&lt;p&gt;This edition collects the updates, experiments, technical discussions, and releases worth paying attention to — from frontend frameworks and tooling changes to AI coding workflows, browser platform news, and interesting projects from across the developer ecosystem.&lt;/p&gt;
&lt;p&gt;Whether you are following React, TypeScript, Bun, Node.js, AI-assisted development, or simply looking for useful tools to explore, here are the links that stood out this week.&lt;/p&gt;
&lt;h2&gt;📢 Special partner message&lt;/h2&gt;
&lt;h3&gt;🗓️ &lt;a href=&quot;https://reactnorway.com&quot;&gt;React Norway&lt;/a&gt; — Oslo, June 5 🇳🇴&lt;/h3&gt;
&lt;p&gt;Join 350+ React and full-stack developers for a unique single-track conference with a strong “Rock &amp;amp; React” vibe, great talks, community energy, and modern web engineering discussions.&lt;/p&gt;
&lt;h3&gt;🎟️ Use code &lt;code&gt;TWIR&lt;/code&gt; for 10% off your ticket.&lt;/h3&gt;
&lt;h2&gt;🧠 Language &amp;amp; Runtime Updates&lt;/h2&gt;
&lt;h3&gt;TypeScript 6.0 Is Official — And It’s Preparing the Road to TypeScript 7&lt;/h3&gt;
&lt;p&gt;One of the biggest ecosystem updates remains &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html&quot;&gt;TypeScript 6.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This release is not just another incremental version bump. It acts as a transition layer between the current JavaScript-based compiler and the upcoming native TypeScript 7 compiler. According to the TypeScript team, 6.0 is effectively the bridge release that prepares projects for the future architecture.&lt;/p&gt;
&lt;p&gt;Some notable changes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;alignment with future TS 7 behavior&lt;/li&gt;
&lt;li&gt;deprecations aimed at modern ESM workflows&lt;/li&gt;
&lt;li&gt;simplified DOM library behavior&lt;/li&gt;
&lt;li&gt;stricter compiler rules&lt;/li&gt;
&lt;li&gt;migration tooling for upcoming changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The broader direction is clear: TypeScript is increasingly optimizing for modern evergreen runtimes, ESM tooling, and native performance improvements.&lt;/p&gt;
&lt;h3&gt;Bun’s Rust Rewrite Has Been Merged&lt;/h3&gt;
&lt;p&gt;The Bun story continues.&lt;/p&gt;
&lt;p&gt;One of the more talked-about updates recently: &lt;a href=&quot;https://github.com/oven-sh/bun/pull/30412&quot;&gt;Bun’s experimental Rust rewrite effort has now been merged&lt;/a&gt;, generating substantial discussion across developer communities. Questions quickly emerged around maintainability, AI-assisted code generation quality, and long-term runtime strategy.&lt;/p&gt;
&lt;p&gt;At the same time, Bun keeps strengthening its position inside the TypeScript ecosystem.&lt;/p&gt;
&lt;p&gt;Developers increasingly evaluate Bun not just as a runtime, but as a broader replacement for parts of the traditional Node toolchain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;package manager&lt;/li&gt;
&lt;li&gt;bundler&lt;/li&gt;
&lt;li&gt;test runner&lt;/li&gt;
&lt;li&gt;TypeScript execution environment&lt;/li&gt;
&lt;li&gt;server runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The “Should new TypeScript projects start with Bun?” conversation is becoming more common.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/deno.land/post/3mm6clkq5uc22&quot;&gt;Deno 2.8 ships this week&lt;/a&gt; with a focus on improving the developer experience around TypeScript, ESM, and modern JavaScript features.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/install-element-ot&quot;&gt;Install web apps with the new HTML install element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/prompts-advisory-structure-binding-daniel-meyer-cpxce/&quot;&gt;Prompts are advisory. Structure is binding.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://grahamhelton.com/blog/ssh-cheatsheet&quot;&gt;An Excruciatingly Detailed Guide To SSH&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://typesetinthefuture.com/2016/02/18/futuristic/&quot;&gt;How To Make Your Text Look Futuristic&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.thenodebook.com/&quot;&gt;MASTER THE NODE.JS INTERNALS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/using-nonexistent-nth-letter-selector-now/&quot;&gt;Let’s Use the Nonexistent ::nth-letter Selector Now&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tanstack.com/blog/incident-followup&quot;&gt;Hardening TanStack After the npm Compromise&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://polypane.app/blog/using-safe-area-inset-to-build-mobile-safe-layouts/&quot;&gt;Using safe-area-inset to build mobile-safe layouts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejsdesignpatterns.com/blog/whats-new-in-nodejs-26/&quot;&gt;What&apos;s new in Node.js 26&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.sentry.io/fixing-javascript-observability/&quot;&gt;Fixing JavaScript observability, one library at a time&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.gaborkoos.com/posts/2026-05-09-Your-Recursion-Is-Lying-to-You/&quot;&gt;Your Recursion Is Lying to You&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jasnell.me/posts/quic-part-4&quot;&gt;HTTP/3 Over QUIC in Node.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rubrik.com/blog/architecture/26/2/async-react-building-non-blocking-uis-with-usetransition-and-useactionstate&quot;&gt;Async React: Building Non-Blocking UIs with useTransition and useActionState&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ishadeed.com/article/css-round/&quot;&gt;Better fluid sizing with round()&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jvns.ca/blog/2026/05/15/moving-away-from-tailwind--and-learning-to-structure-my-css-/&quot;&gt;Moving away from Tailwind, and learning to structure my CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://evilmartians.com/chronicles/600-million-people-write-right-to-left-2-fixes-your-app-needs&quot;&gt;600+ million people write right-to-left: 2 fixes your app needs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/gap-decorations-stable?hl=en&quot;&gt;Gap decorations: Now available in Chromium&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://chrismorgan.info/css-themed-colours&quot;&gt;A few ways of specifying per-theme colours in only CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/when-to-use-and-not-use-css-shorthand-properties&quot;&gt;When to use (and not use) CSS shorthand properties&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tympanus.net/codrops/2025/02/04/how-to-make-the-fluffiest-grass-with-three-js/&quot;&gt;How to Make The Fluffiest Grass With Three.js&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;Open-source Discord alternative &lt;a href=&quot;https://github.com/Milkshiift/GoofCord&quot;&gt;GoofCord&lt;/a&gt; has been released, promising a faster, cleaner, and far more customizable experience than the official client.&lt;/p&gt;
&lt;p&gt;According to the project, GoofCord:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;runs noticeably faster than the standard Discord client, with fewer slowdowns and UI hiccups;&lt;/li&gt;
&lt;li&gt;blocks built-in telemetry and user data collection;&lt;/li&gt;
&lt;li&gt;supports password-encrypted conversations;&lt;/li&gt;
&lt;li&gt;allows screen sharing at any resolution and frame rate;&lt;/li&gt;
&lt;li&gt;lets users choose which application audio gets streamed;&lt;/li&gt;
&lt;li&gt;automatically updates your status based on games, music, or videos;&lt;/li&gt;
&lt;li&gt;supports Vencord, Equicord, and Shelter customization plugins out of the box;&lt;/li&gt;
&lt;li&gt;includes global hotkeys that keep working even when the app is minimized;&lt;/li&gt;
&lt;li&gt;supports audio streaming on Linux, while also running on Windows and macOS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/alternbits/awesome-cuda-books&quot;&gt;Awesome CUDA Books&lt;/a&gt; is a new curated list of resources for learning CUDA programming, covering everything from beginner-friendly introductions to advanced optimization techniques.&lt;/p&gt;
&lt;p&gt;An open-source project called &lt;a href=&quot;https://github.com/MikeVeerman/tokenspeed&quot;&gt;tokenspeed&lt;/a&gt; (including an &lt;a href=&quot;https://mikeveerman.github.io/tokenspeed/?rate=30&amp;amp;mode=code&quot;&gt;online version&lt;/a&gt;) has been released to make LLM token throughput easier to understand visually.&lt;/p&gt;
&lt;p&gt;Most local LLM benchmarks report raw generation speed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;47 tokens/sec on an M3&lt;/li&gt;
&lt;li&gt;180 tokens/sec on an RTX 4090&lt;/li&gt;
&lt;li&gt;500 tokens/sec on Groq&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But unless you&apos;ve actually watched tokens stream at those speeds, those numbers can feel pretty abstract.&lt;/p&gt;
&lt;p&gt;tokenspeed solves that problem.&lt;/p&gt;
&lt;p&gt;It’s a terminal utility that simulates token streaming at any speed you choose, allowing you to see what different throughput numbers actu…</content:encoded></item><item><title>How JavaScript call, apply, and bind Actually Work</title><link>https://jsdev.space/howto/call-apply-bind/</link><guid isPermaLink="true">https://jsdev.space/howto/call-apply-bind/</guid><description>Deep dive into JavaScript call, apply, bind, and arguments. Learn explicit binding, handwritten implementations, and real language internals.</description><pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaScript gives functions a strange amount of power.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/call-apply-bind-deep-dive.png&quot; alt=&quot;call, apply, bind, and arguments&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A function can be called directly. It can be stored in a variable. It can be passed into another function. It can become a method of an object. It can be used as a constructor. It can even be executed with a completely different &lt;code&gt;this&lt;/code&gt; value from the one you expected.&lt;/p&gt;
&lt;p&gt;That flexibility is one of the reasons JavaScript is so expressive. It is also one of the reasons &lt;code&gt;this&lt;/code&gt; has confused developers for decades.&lt;/p&gt;
&lt;p&gt;The methods &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, and &lt;code&gt;bind&lt;/code&gt; sit right in the middle of that confusion. Most developers know their surface-level syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;functionName.call(context, arg1, arg2)
functionName.apply(context, [arg1, arg2])
const boundFunction = functionName.bind(context, arg1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But syntax is not understanding.&lt;/p&gt;
&lt;p&gt;Real understanding starts when you can explain why these APIs exist, what invocation rules they rely on, how to implement simplified versions yourself, and where the edge cases begin.&lt;/p&gt;
&lt;p&gt;This article takes a deep look at &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, &lt;code&gt;bind&lt;/code&gt;, and &lt;code&gt;arguments&lt;/code&gt; from the perspective of JavaScript language mechanics. The goal is not to memorize another interview trick. The goal is to understand what actually happens when a function is invoked.&lt;/p&gt;
&lt;h2&gt;Why These APIs Exist&lt;/h2&gt;
&lt;p&gt;The main problem solved by &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, and &lt;code&gt;bind&lt;/code&gt; is context control.&lt;/p&gt;
&lt;p&gt;In JavaScript, &lt;code&gt;this&lt;/code&gt; is not determined only by where a function is written. For regular functions, &lt;code&gt;this&lt;/code&gt; is mainly determined by how the function is called.&lt;/p&gt;
&lt;p&gt;That is the part many developers miss.&lt;/p&gt;
&lt;p&gt;Consider this function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function showCurrentUser() {
  console.log(this.username)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is no permanent &lt;code&gt;this&lt;/code&gt; value inside this function. The value depends on the invocation pattern.&lt;/p&gt;
&lt;p&gt;Call it directly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;showCurrentUser()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In non-strict browser code, &lt;code&gt;this&lt;/code&gt; may point to &lt;code&gt;window&lt;/code&gt;. In strict mode, &lt;code&gt;this&lt;/code&gt; will be &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Call it as an object method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const account = {
  username: &apos;Maya&apos;,
  showCurrentUser,
}

account.showCurrentUser()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;this&lt;/code&gt; points to &lt;code&gt;account&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Call it with &lt;code&gt;call&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;showCurrentUser.call({ username: &apos;Nina&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;this&lt;/code&gt; points to the object passed into &lt;code&gt;call&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Same function. Different result. Different invocation.&lt;/p&gt;
&lt;p&gt;That is why the best mental model is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most &lt;code&gt;this&lt;/code&gt; bugs are invocation bugs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not declaration bugs. Not class bugs. Not file structure bugs. Invocation bugs.&lt;/p&gt;
&lt;h2&gt;The Binding Rules Behind &lt;code&gt;this&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Before implementing &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, or &lt;code&gt;bind&lt;/code&gt;, we need to understand the binding rules they interact with.&lt;/p&gt;
&lt;p&gt;JavaScript has several important ways to determine &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Default Binding&lt;/h3&gt;
&lt;p&gt;Default binding happens when a regular function is called without an owning object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function printContext() {
  console.log(this)
}

printContext()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In strict mode, &lt;code&gt;this&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use strict&apos;

function printContext() {
  console.log(this)
}

printContext() // undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In older non-strict browser code, &lt;code&gt;this&lt;/code&gt; can fall back to the global object.&lt;/p&gt;
&lt;p&gt;Modern code should avoid relying on this behavior. It is fragile and often different between environments.&lt;/p&gt;
&lt;h3&gt;Implicit Binding&lt;/h3&gt;
&lt;p&gt;Implicit binding happens when a function is called as a property of an object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const dashboard = {
  title: &apos;Admin Panel&apos;,
  printTitle() {
    console.log(this.title)
  },
}

dashboard.printTitle()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The call expression is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dashboard.printTitle()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the function is called through &lt;code&gt;dashboard&lt;/code&gt;, JavaScript binds &lt;code&gt;this&lt;/code&gt; to &lt;code&gt;dashboard&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This rule is extremely important because a simplified implementation of &lt;code&gt;call&lt;/code&gt; can exploit it.&lt;/p&gt;
&lt;h3&gt;Explicit Binding&lt;/h3&gt;
&lt;p&gt;Explicit binding happens when you use &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, or &lt;code&gt;bind&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function printRole() {
  console.log(this.role)
}

printRole.call({ role: &apos;editor&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the function is not called as a method of the object. We explicitly provide the object that should become &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That is the purpose of &lt;code&gt;call&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt;: execute a function immediately with a chosen &lt;code&gt;this&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bind&lt;/code&gt; also chooses a &lt;code&gt;this&lt;/code&gt; value, but it does not execute immediately. It returns a new function that remembers the chosen context.&lt;/p&gt;
&lt;h3&gt;Constructor Binding&lt;/h3&gt;
&lt;p&gt;Constructor binding happens when a function is called with &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Product(name) {
  this.name = name
}

const book = new Product(&apos;JavaScript Guide&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;new Product(...)&lt;/code&gt; runs, JavaScript creates a new object and binds &lt;code&gt;this&lt;/code&gt; to that new object during the constructor call.&lt;/p&gt;
&lt;p&gt;This matters because constructor binding has higher priority than a simple bound context. A complete &lt;code&gt;bind&lt;/code&gt; polyfill must handle this case. Many simplified examples do not.&lt;/p&gt;
&lt;h2&gt;The Basic Difference Between call, apply, and bind&lt;/h2&gt;
&lt;p&gt;At a high level, the three APIs differ in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Whether the function executes immediately.&lt;/li&gt;
&lt;li&gt;How arguments are passed.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;fn.call(context, arg1, arg2, arg3)
fn.apply(context, [arg1, arg2, arg3])
const later = fn.bind(context, arg1, arg2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;call&lt;/code&gt; executes immediately and receives arguments one by one.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apply&lt;/code&gt; executes immediately and receives arguments as an array-like list.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bind&lt;/code&gt; does not execute immediately. It returns a new function with a fixed context and optional preset arguments.&lt;/p&gt;
&lt;p&gt;That sounds simple, but the real mechanics are more interesting.&lt;/p&gt;
&lt;h2&gt;Implementing a Simple call&lt;/h2&gt;
&lt;p&gt;Let us start from scratch.&lt;/p&gt;
&lt;p&gt;All functions inherit from &lt;code&gt;Function.prototype&lt;/code&gt;, so we can add our own method there for educational purposes.&lt;/p&gt;
&lt;p&gt;Do not patch built-in prototypes in production code. This is only for learning.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Function.prototype.runNow = function () {
  console.log(&apos;custom method executed&apos;)
}

function calculateTotal() {
  console.log(&apos;original function executed&apos;)
}

calculateTotal.runNow()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;custom method executed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it does not execute &lt;code&gt;calculateTotal&lt;/code&gt; itself.&lt;/p&gt;
&lt;p&gt;Inside &lt;code&gt;runNow&lt;/code&gt;, what does &lt;code&gt;this&lt;/code&gt; refer to?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Function.prototype.runNow = function () {
  console.log(this)
}

calculateTotal.runNow()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value of &lt;code&gt;this&lt;/code&gt; is the function that called &lt;code&gt;runNow&lt;/code&gt;. In this case, &lt;code&gt;calculateTotal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That means we can execute it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Function.prototype.runNow = function () {
  const targetFunction = this
  return targetFunction()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function sayHello() {
  return &apos;Hello&apos;
}

console.log(sayHello.runNow())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have recreated the first part of &lt;code&gt;call&lt;/code&gt;: invoking the function through a prototype method.&lt;/p&gt;
&lt;p&gt;Now we need to control &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How call Changes this&lt;/h2&gt;
&lt;p&gt;Suppose we want this behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function printUser() {
  console.log(this.n…</content:encoded></item><item><title>Howto Master the Blob for File Handling and Memory Optimization</title><link>https://jsdev.space/howto/master-blob-api/</link><guid isPermaLink="true">https://jsdev.space/howto/master-blob-api/</guid><description>Learn how to use the Blob API for file processing, uploads, previews, downloads, and memory optimization in modern JavaScript applications.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern frontend applications deal with files all the time.&lt;/p&gt;
&lt;p&gt;Users upload avatars, drop videos into dashboards, export CSV reports, preview PDFs, download generated configuration files, and work with media directly in the browser. At first, these features look simple. A file input, a preview element, maybe a download button, and the job seems done.&lt;/p&gt;
&lt;p&gt;Then the real problems appear.&lt;/p&gt;
&lt;p&gt;Large files freeze the tab. Image uploads are too slow. Data URLs become huge. Browser memory keeps growing. Previews work for one file type but not another. A dashboard that looked fine during testing becomes unstable after a user processes dozens of images in a single session.&lt;/p&gt;
&lt;p&gt;This is where the &lt;code&gt;Blob&lt;/code&gt; API becomes important.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Blob&lt;/code&gt; is not just a small browser feature for downloads. It is one of the core building blocks behind file handling in modern JavaScript. Once you understand how Blob objects work, you can build cleaner upload flows, safer previews, better export tools, and more memory-efficient interfaces.&lt;/p&gt;
&lt;p&gt;This article walks through practical Blob usage in real frontend work: creating Blob objects, processing large files in chunks, compressing images, building previews, generating downloads, and avoiding memory leaks caused by forgotten object URLs.&lt;/p&gt;
&lt;h2&gt;What Is a Blob?&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;Blob&lt;/code&gt; is an immutable object that represents raw data.&lt;/p&gt;
&lt;p&gt;That data can be text, JSON, CSV, an image, a video, a PDF, or any other binary content the browser can hold. The word “Blob” stands for Binary Large Object, but in frontend development you can think of it as a file-like container created or handled inside the browser.&lt;/p&gt;
&lt;p&gt;A minimal Blob looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const textBlob = new Blob([&apos;Hello from the Blob API&apos;], {
  type: &apos;text/plain&apos;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first argument is an array of data parts. The second argument describes the Blob. Most of the time, the most important option is &lt;code&gt;type&lt;/code&gt;, which stores the MIME type.&lt;/p&gt;
&lt;p&gt;Examples of common MIME types:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;text/plain
application/json
text/csv
image/png
image/jpeg
application/pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The MIME type helps the browser understand how to treat the data. A JSON Blob, an image Blob, and a PDF Blob may all be Blob objects, but the browser can preview, download, or send them differently depending on the type.&lt;/p&gt;
&lt;h2&gt;Why Blob Is Better Than Large Data URLs&lt;/h2&gt;
&lt;p&gt;A common beginner approach for generating files is to create a &lt;code&gt;data:&lt;/code&gt; URL manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const hugeText = &apos;Some large content...&apos;.repeat(100_000);

const url =
  &apos;data:text/plain;charset=utf-8,&apos; +
  encodeURIComponent(hugeText);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works for small examples, but it scales badly. Large data URLs can consume more memory than expected, become difficult for the browser to handle, and fail when the generated URL becomes too long.&lt;/p&gt;
&lt;p&gt;Blob is usually the better option:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const hugeText = &apos;Some large content...&apos;.repeat(100_000);

const blob = new Blob([hugeText], {
  type: &apos;text/plain&apos;,
});

const url = URL.createObjectURL(blob);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of encoding everything into a giant string URL, the browser creates an object URL that points to the Blob data internally. That is cleaner, more memory-friendly, and more reliable for larger content.&lt;/p&gt;
&lt;h2&gt;Creating Blob Objects Properly&lt;/h2&gt;
&lt;p&gt;Blob creation is simple, but in real projects it is worth wrapping it in small utilities so your code stays consistent.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type BlobOptionsInput = {
  mimeType?: string;
};

function createFileBlob(
  parts: BlobPart[],
  options: BlobOptionsInput = {}
) {
  return new Blob(parts, {
    type: options.mimeType ?? &apos;text/plain&apos;,
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can create different file-like objects with explicit MIME types.&lt;/p&gt;
&lt;h3&gt;Text Blob&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const readmeBlob = createFileBlob(
  [&apos;This file was generated in the browser.&apos;],
  { mimeType: &apos;text/plain&apos; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;JSON Blob&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const userSettings = {
  theme: &apos;dark&apos;,
  compactMode: true,
  language: &apos;en&apos;,
};

const settingsBlob = createFileBlob(
  [JSON.stringify(userSettings, null, 2)],
  { mimeType: &apos;application/json&apos; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HTML Blob&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const htmlDocument = `
  &amp;lt;!doctype html&amp;gt;
  &amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;h1&amp;gt;Generated HTML&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
`;

const htmlBlob = createFileBlob(
  [htmlDocument],
  { mimeType: &apos;text/html&apos; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main rule is simple: always set the correct MIME type when you know what kind of data you are creating.&lt;/p&gt;
&lt;h2&gt;Downloading Generated Files in the Browser&lt;/h2&gt;
&lt;p&gt;One of the most common Blob use cases is generating downloadable files without asking the server to create them.&lt;/p&gt;
&lt;p&gt;For example, a settings page may allow users to export their configuration as JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function downloadJsonFile(
  data: unknown,
  filename = &apos;settings.json&apos;
) {
  const json = JSON.stringify(data, null, 2);

  const blob = new Blob([json], {
    type: &apos;application/json&apos;,
  });

  const url = URL.createObjectURL(blob);

  const link = document.createElement(&apos;a&apos;);
  link.href = url;
  link.download = filename;
  link.style.display = &apos;none&apos;;

  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);

  URL.revokeObjectURL(url);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;downloadJsonFile(
  {
    editor: &apos;VS Code&apos;,
    theme: &apos;dark&apos;,
    autosave: true,
  },
  &apos;developer-settings.json&apos;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern is useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;exporting user settings&lt;/li&gt;
&lt;li&gt;downloading generated reports&lt;/li&gt;
&lt;li&gt;creating local backups&lt;/li&gt;
&lt;li&gt;exporting analytics data&lt;/li&gt;
&lt;li&gt;generating JSON, CSV, or text files from client-side state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The important part is cleanup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;URL.revokeObjectURL(url);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whenever you create an object URL, you should also decide when to revoke it.&lt;/p&gt;
&lt;h2&gt;Processing Large Files in Chunks&lt;/h2&gt;
&lt;p&gt;Reading a large file all at once can hurt performance.&lt;/p&gt;
&lt;p&gt;This looks harmless:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const reader = new FileReader();

reader.onload = () =&amp;gt; {
  const content = reader.result;
  console.log(content);
};

reader.readAsText(file);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For small files, this is fine. For large files, it can freeze the page or consume too much memory.&lt;/p&gt;
&lt;p&gt;The safer pattern is chunked processing.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;File&lt;/code&gt; is a kind of Blob, so it supports the &lt;code&gt;slice()&lt;/code&gt; method. This allows you to read a file piece by piece.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function processFileInChunks(
  file: File,
  chunkSize = 1024 * 1024
) {
  const totalChunks = Math.ceil(file.size / chunkSize);

  for (let chunkIndex = 0; chunkIndex &amp;lt; totalChunks; chunkIndex++) {
    const start = chunkIndex * chunkSize;
    const end = Math.min(start + chunkSize, file.size);

    const chunk = file.slice(start, end);

    await processChunk(chunk, chunkIndex, totalChunks);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A simple chunk reader can look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function readBlobAsText(blob: Blob): Promise&amp;lt;string&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    const reader = new FileReader();

    reader.onload = () =&amp;gt; {
      resolve(String(reader.result ?? &apos;&apos;));
    };

    reader.onerror = () =&amp;gt; {
      reject(reader.error);
    };

    reader.readAsText(blob);
  });
}

async function processChunk(
  chunk: Blob,
  index: number,
  total: number
) {
  const text = await readBlobAsText(chunk);

  console.log({
    index,
    total,
    size: chunk.size,
    preview: text.slice(0, 80),
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach gives you more control over memory usage and makes it easier to show progress.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function processFileWithProgress(
  file: File,
  onProgress: (percentage: number) =&amp;gt; void
) {
  const chunkSize = 1024 * 1024;
  const totalChunks = Math.ceil(file.size / chunkSize);

  for (let index =…</content:encoded></item><item><title>Howto Undo Git Commits Without Breaking Your History</title><link>https://jsdev.space/howto/undo-git-commits/</link><guid isPermaLink="true">https://jsdev.space/howto/undo-git-commits/</guid><description>Learn when to use Git reset, revert, restore, amend, and reflog to safely undo commits, recover files, and fix Git mistakes.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Almost every Git user eventually runs into the same problem.&lt;/p&gt;
&lt;p&gt;A commit was created.&lt;/p&gt;
&lt;p&gt;Then regret arrived.&lt;/p&gt;
&lt;p&gt;Maybe the commit contains broken code. Maybe the wrong file slipped in. Maybe the commit message is terrible. Or maybe the commit was already pushed before anyone noticed the mistake.&lt;/p&gt;
&lt;p&gt;At that point, people usually search:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How do I undo a Git commit?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./images/undo-git-commit.png&quot; alt=&quot;Undo Git Commit&quot; title=&quot;Undo Git Commit&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then the confusion begins.&lt;/p&gt;
&lt;p&gt;Some tutorials recommend &lt;code&gt;git reset&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Others say &lt;code&gt;git revert&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Someone suggests &lt;code&gt;checkout&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then suddenly &lt;code&gt;restore&lt;/code&gt;, &lt;code&gt;amend&lt;/code&gt;, &lt;code&gt;reflog&lt;/code&gt;, &lt;code&gt;HEAD~1&lt;/code&gt;, force push, and several unfamiliar Git concepts appear all at once.&lt;/p&gt;
&lt;p&gt;The frustrating part is that most of those answers are technically correct.&lt;/p&gt;
&lt;p&gt;The real problem is simpler:&lt;/p&gt;
&lt;p&gt;“Undoing a commit” can mean several completely different things.&lt;/p&gt;
&lt;p&gt;You might want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remove the latest commit&lt;/li&gt;
&lt;li&gt;remove a commit but keep your changes&lt;/li&gt;
&lt;li&gt;undo something already pushed&lt;/li&gt;
&lt;li&gt;restore a file&lt;/li&gt;
&lt;li&gt;rewrite the previous commit&lt;/li&gt;
&lt;li&gt;recover deleted history&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git provides different tools for each situation.&lt;/p&gt;
&lt;p&gt;Once you understand that distinction, the command overload becomes much easier to understand.&lt;/p&gt;
&lt;h2&gt;What a Commit Actually Is&lt;/h2&gt;
&lt;p&gt;A Git commit is simply a saved snapshot of your project at a specific point in time.&lt;/p&gt;
&lt;p&gt;If you&apos;ve played video games, think about save points.&lt;/p&gt;
&lt;p&gt;You reach a stable point, save progress, and continue experimenting.&lt;/p&gt;
&lt;p&gt;Git works in a similar way.&lt;/p&gt;
&lt;p&gt;A commit stores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;project state&lt;/li&gt;
&lt;li&gt;file changes&lt;/li&gt;
&lt;li&gt;author information&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;li&gt;commit message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Commits are then connected together into a history graph.&lt;/p&gt;
&lt;h2&gt;Why “Undo Commit” Causes So Much Confusion&lt;/h2&gt;
&lt;p&gt;This phrase sounds simple, but it hides several very different intentions.&lt;/p&gt;
&lt;p&gt;Sometimes you want to remove the commit &lt;strong&gt;without touching your code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes you want to remove &lt;strong&gt;both the commit and the changes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes you should not remove anything at all — you only need to create a new commit that cancels an earlier one.&lt;/p&gt;
&lt;p&gt;Different problem.&lt;/p&gt;
&lt;p&gt;Different command.&lt;/p&gt;
&lt;p&gt;That is why Git offers multiple approaches instead of a single universal &quot;undo&quot; button.&lt;/p&gt;
&lt;h2&gt;The Fast Cheat Sheet&lt;/h2&gt;
&lt;p&gt;If you want the shortest possible version:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Remove local commit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git reset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undo pushed commit safely&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git revert&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git restore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fix last commit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git commit --amend&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recover deleted history&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git reflog&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Now let’s break down what each command actually does.&lt;/p&gt;
&lt;h2&gt;Remove the Last Local Commit with &lt;code&gt;git reset&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Suppose you just created a commit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -m &quot;Temporary experiment&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few seconds later you realize:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That commit should not exist.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This removes the commit from history but keeps your file changes.&lt;/p&gt;
&lt;p&gt;By default, Git also unstages your files.&lt;/p&gt;
&lt;h3&gt;Understanding &lt;code&gt;HEAD~1&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;HEAD&lt;/code&gt; means:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the commit you are currently on&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;HEAD~1&lt;/code&gt; means:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;one commit earlier&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HEAD~1
HEAD~2
HEAD~3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;move backward through history.&lt;/p&gt;
&lt;h3&gt;Soft Reset&lt;/h3&gt;
&lt;p&gt;If you want to remove the commit but keep staging intact:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --soft HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file changes&lt;/li&gt;
&lt;li&gt;staged files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Useful when you only want to rewrite the commit message or slightly reorganize the commit.&lt;/p&gt;
&lt;h3&gt;Hard Reset&lt;/h3&gt;
&lt;p&gt;The dangerous version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This removes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the commit&lt;/li&gt;
&lt;li&gt;working directory changes&lt;/li&gt;
&lt;li&gt;staged files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your repository becomes identical to the previous commit.&lt;/p&gt;
&lt;p&gt;Use this command carefully.&lt;/p&gt;
&lt;h2&gt;Undo a Pushed Commit with &lt;code&gt;git revert&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Once code has already been pushed, &lt;code&gt;reset&lt;/code&gt; becomes risky.&lt;/p&gt;
&lt;p&gt;History rewriting can break collaborators.&lt;/p&gt;
&lt;p&gt;That is why Git provides:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git revert &amp;lt;commit-hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of deleting history, Git creates a new commit that reverses the previous changes.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A → B → C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git revert C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A → B → C → D
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;D&lt;/code&gt; cancels the changes introduced by &lt;code&gt;C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This makes &lt;code&gt;revert&lt;/code&gt; the safest option for shared repositories.&lt;/p&gt;
&lt;h2&gt;Restore a Single File with &lt;code&gt;git restore&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes the entire commit is fine.&lt;/p&gt;
&lt;p&gt;Only one file is wrong.&lt;/p&gt;
&lt;p&gt;Imagine you broke:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restore it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git replaces the file with the version from the latest commit.&lt;/p&gt;
&lt;p&gt;You can also restore from a specific commit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore --source=HEAD~1 config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore --source=a1b2c3d config.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is extremely useful when recovering earlier implementations.&lt;/p&gt;
&lt;h2&gt;Fix the Last Commit with &lt;code&gt;git commit --amend&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes the commit is mostly correct.&lt;/p&gt;
&lt;p&gt;You simply forgot something.&lt;/p&gt;
&lt;p&gt;Common cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;missing file&lt;/li&gt;
&lt;li&gt;bad commit message&lt;/li&gt;
&lt;li&gt;small bug fix&lt;/li&gt;
&lt;li&gt;forgotten config change&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git opens the commit editor.&lt;/p&gt;
&lt;p&gt;Update the message.&lt;/p&gt;
&lt;p&gt;Save.&lt;/p&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;p&gt;If you forgot a file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add missing-file.ts
git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git rebuilds the previous commit as if it had originally been created correctly.&lt;/p&gt;
&lt;h3&gt;Important Warning&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;amend&lt;/code&gt; rewrites history.&lt;/p&gt;
&lt;p&gt;If the commit was already pushed, you may need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push --force
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use force push carefully.&lt;/p&gt;
&lt;h2&gt;Recover Deleted Work with &lt;code&gt;git reflog&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This command rescues many Git disasters.&lt;/p&gt;
&lt;p&gt;Especially after accidental:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reflog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;7f2a3c1 HEAD@{0}: reset: moving to HEAD~1
3b9d221 HEAD@{1}: commit: Add payment logic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice something important.&lt;/p&gt;
&lt;p&gt;Your &quot;deleted&quot; commit is usually still recoverable.&lt;/p&gt;
&lt;p&gt;Grab the hash:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard 3b9d221
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Repository restored.&lt;/p&gt;
&lt;p&gt;Panic avoided.&lt;/p&gt;
&lt;h2&gt;Undo &lt;code&gt;git add&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Technically not a commit operation.&lt;/p&gt;
&lt;p&gt;Still useful.&lt;/p&gt;
&lt;p&gt;If you staged the wrong file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unstage it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore --staged package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file remains modified.&lt;/p&gt;
&lt;p&gt;Git simply removes it from staging.&lt;/p&gt;
&lt;h2&gt;Which Command Should You Use?&lt;/h2&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when removing local commits.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git revert
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when undoing pushed commits.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git restore
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;for recovering files.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --amend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;for repairing recent commits.&lt;/p&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git…</content:encoded></item><item><title>GPT Shortcuts That Actually Work</title><link>https://jsdev.space/gpt-shortcuts-that-work/</link><guid isPermaLink="true">https://jsdev.space/gpt-shortcuts-that-work/</guid><description>Learn which GPT shortcuts genuinely improve responses, which are unreliable, and which “secret commands” are complete myths.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TikTok, YouTube, Reddit, and Telegram are full of so-called “secret GPT commands.”&lt;/p&gt;
&lt;p&gt;People constantly share prompts like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IQ200
/UNFILTER
X10THINK
/RAW
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually claiming they unlock hidden reasoning modes, bypass restrictions, or somehow make the model dramatically smarter.&lt;/p&gt;
&lt;p&gt;Most of these claims are exaggerated or completely false.&lt;/p&gt;
&lt;p&gt;There is no hidden “superintelligence mode.” No secret admin console. No magical phrase that suddenly transforms a language model into something fundamentally different.&lt;/p&gt;
&lt;p&gt;At the same time, some GPT shortcuts really are useful. Not because they unlock hidden systems, but because they communicate intent more clearly and efficiently.&lt;/p&gt;
&lt;p&gt;That distinction matters.&lt;/p&gt;
&lt;p&gt;This article breaks common GPT shortcuts into three groups:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;shortcuts that genuinely help&lt;/li&gt;
&lt;li&gt;shortcuts that work inconsistently&lt;/li&gt;
&lt;li&gt;shortcuts that are basically internet mythology&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not reverse engineering AI models. It is understanding which prompt patterns actually improve day-to-day work with language models.&lt;/p&gt;
&lt;h2&gt;Why GPT Shortcuts Sometimes Work&lt;/h2&gt;
&lt;p&gt;One of the biggest misconceptions about ChatGPT is the idea that it behaves like a terminal or command interpreter.&lt;/p&gt;
&lt;p&gt;It does not.&lt;/p&gt;
&lt;p&gt;When someone writes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/REDTEAM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the model is not switching into a hidden mode.&lt;/p&gt;
&lt;p&gt;Instead, it recognizes a familiar pattern from its training data and continues the conversation in a style associated with that pattern.&lt;/p&gt;
&lt;p&gt;That is an important difference.&lt;/p&gt;
&lt;p&gt;GPT shortcuts are essentially compact prompt instructions.&lt;/p&gt;
&lt;p&gt;Some work well because they clearly describe the desired tone, structure, or behavior in a very short format.&lt;/p&gt;
&lt;p&gt;Others fail because they are vague internet myths with no meaningful instruction behind them.&lt;/p&gt;
&lt;p&gt;Another important detail is that model behavior is probabilistic.&lt;/p&gt;
&lt;p&gt;A shortcut might work perfectly ten times in a row and then suddenly get ignored on the next attempt.&lt;/p&gt;
&lt;p&gt;That inconsistency is normal.&lt;/p&gt;
&lt;h2&gt;EL5 Is Still One of the Most Useful Shortcuts&lt;/h2&gt;
&lt;p&gt;The classic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;stands for:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Explain Like I&apos;m 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This remains one of the most reliable shortcuts because it strongly influences how the model structures information.&lt;/p&gt;
&lt;p&gt;Responses usually become:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simpler&lt;/li&gt;
&lt;li&gt;shorter&lt;/li&gt;
&lt;li&gt;more conversational&lt;/li&gt;
&lt;li&gt;example-driven&lt;/li&gt;
&lt;li&gt;less technical&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL5: Explain Docker containers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will usually produce analogies and simplified explanations instead of implementation details and infrastructure terminology.&lt;/p&gt;
&lt;p&gt;For onboarding, teaching, and documentation, this shortcut is genuinely useful.&lt;/p&gt;
&lt;h2&gt;EL10 and EL15 Are Less Predictable&lt;/h2&gt;
&lt;p&gt;Variants like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL10
EL15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;try to simulate different levels of understanding.&lt;/p&gt;
&lt;p&gt;Sometimes they work reasonably well.&lt;/p&gt;
&lt;p&gt;Sometimes they barely change the output.&lt;/p&gt;
&lt;p&gt;The problem is that language models do not maintain a strict internal “age scale” for explanations.&lt;/p&gt;
&lt;p&gt;If the goal is more advanced output, direct instructions are usually more effective.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Explain this like you&apos;re speaking to a senior backend engineer.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is typically more reliable than:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EL20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which often changes very little.&lt;/p&gt;
&lt;h2&gt;/STEP-BY-STEP Is Genuinely Helpful&lt;/h2&gt;
&lt;p&gt;One of the most practical shortcuts is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/STEP-BY-STEP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This encourages the model to break problems into stages instead of jumping directly to conclusions.&lt;/p&gt;
&lt;p&gt;It works especially well for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;programming&lt;/li&gt;
&lt;li&gt;debugging&lt;/li&gt;
&lt;li&gt;mathematics&lt;/li&gt;
&lt;li&gt;architecture discussions&lt;/li&gt;
&lt;li&gt;learning workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/STEP-BY-STEP Debug this React hydration error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually produces a much clearer and more structured response than a generic debugging request.&lt;/p&gt;
&lt;p&gt;This shortcut often improves reasoning visibility significantly.&lt;/p&gt;
&lt;h2&gt;/REDTEAM Produces Better Criticism&lt;/h2&gt;
&lt;p&gt;Another genuinely useful shortcut is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/REDTEAM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shifts the model toward critique instead of agreement.&lt;/p&gt;
&lt;p&gt;The response becomes more skeptical, risk-focused, and analytical.&lt;/p&gt;
&lt;p&gt;Useful scenarios include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API security reviews&lt;/li&gt;
&lt;li&gt;architecture analysis&lt;/li&gt;
&lt;li&gt;business risks&lt;/li&gt;
&lt;li&gt;performance bottlenecks&lt;/li&gt;
&lt;li&gt;edge-case evaluation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/REDTEAM Analyze security problems in this authentication flow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In many cases, this produces much more valuable feedback than standard prompting.&lt;/p&gt;
&lt;h2&gt;/HUMAN Can Improve Tone&lt;/h2&gt;
&lt;p&gt;One of the most common complaints about AI-generated writing is that it sounds robotic.&lt;/p&gt;
&lt;p&gt;The shortcut:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/HUMAN
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sometimes helps reduce that problem.&lt;/p&gt;
&lt;p&gt;It tends to push responses toward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more conversational language&lt;/li&gt;
&lt;li&gt;shorter sentences&lt;/li&gt;
&lt;li&gt;softer transitions&lt;/li&gt;
&lt;li&gt;less corporate phrasing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It does not magically produce perfect human writing, but it can noticeably improve tone in casual content.&lt;/p&gt;
&lt;h2&gt;Formatting Shortcuts Are Usually Reliable&lt;/h2&gt;
&lt;p&gt;Formatting commands tend to work better than abstract “thinking mode” prompts because they clearly define the expected output structure.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/BULLET
/TABLE
/CONCISE
/DETAIL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These shortcuts are practical because they solve real formatting problems.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/TABLE Compare React, Vue, and Svelte
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually generates a clean comparison table immediately.&lt;/p&gt;
&lt;p&gt;Similarly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/CONCISE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;often reduces filler and shortens explanations.&lt;/p&gt;
&lt;p&gt;While:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/DETAIL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;typically increases elaboration and context depth.&lt;/p&gt;
&lt;p&gt;These are among the most useful prompt shortcuts for everyday work.&lt;/p&gt;
&lt;h2&gt;/CODE Is Useful for Development Work&lt;/h2&gt;
&lt;p&gt;Another practical shortcut is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/CODE python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/CODE js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This usually shifts the response toward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more implementation&lt;/li&gt;
&lt;li&gt;less explanation&lt;/li&gt;
&lt;li&gt;cleaner code-focused output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is especially useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prototyping&lt;/li&gt;
&lt;li&gt;boilerplate generation&lt;/li&gt;
&lt;li&gt;quick scripting&lt;/li&gt;
&lt;li&gt;debugging snippets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Most “Thinking Mode” Commands Are Overhyped&lt;/h2&gt;
&lt;p&gt;This is where things become much less impressive.&lt;/p&gt;
&lt;p&gt;Commands like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X10THINK
X5THINK
IQ200
IQ150
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;are mostly placebo.&lt;/p&gt;
&lt;p&gt;People often believe these prompts activate hidden reasoning systems or force the model to “think harder.”&lt;/p&gt;
&lt;p&gt;That is not really what happens.&lt;/p&gt;
&lt;p&gt;In practice, the model usually just:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;writes longer responses&lt;/li&gt;
&lt;li&gt;uses more complicated language&lt;/li&gt;
&lt;li&gt;sounds more dramatic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no hidden intelligence switch.&lt;/p&gt;
&lt;p&gt;Typing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IQ200
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;does not suddenly make the model more intelligent.&lt;/p&gt;
&lt;p&gt;The output may sound more sophisticated, but sounding smarter and reasoning better are not the same thing.&lt;/p&gt;
&lt;h2&gt;/UNFILTER Is Mostly Internet Mythology&lt;/h2&gt;
&lt;p&gt;Probably the most famous fake shortcut is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/UNFILTER
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;People constantly claim it disables moderation systems or removes model restrictions.&lt;/p&gt;
&lt;p&gt;It does not.&lt;/p&gt;
&lt;p&gt;Modern language models do not expose a secret “disable safety” switch through prompts.&lt;/p&gt;
&lt;p&gt;At best, the model may slightly change tone or formatting.&lt;/p&gt;
&lt;p&gt;In most cases, nothing meaningful happens.&lt;/p&gt;
&lt;p&gt;The same applies to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/RAW
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which supposedly reveals hidden prompts, internal state, o…</content:encoded></item><item><title>Best React Libraries and Tools in 2026</title><link>https://jsdev.space/react-stack-2026/</link><guid isPermaLink="true">https://jsdev.space/react-stack-2026/</guid><description>A complete guide to the modern React ecosystem in 2026, covering frameworks, forms, state management, UI libraries, testing, and developer tooling.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;The Modern React Stack Explained for 2026&lt;/h2&gt;
&lt;p&gt;The React ecosystem changes faster than almost any other frontend ecosystem.&lt;/p&gt;
&lt;p&gt;A few years ago, most projects looked nearly identical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create React App&lt;/li&gt;
&lt;li&gt;Redux&lt;/li&gt;
&lt;li&gt;Material UI&lt;/li&gt;
&lt;li&gt;Formik&lt;/li&gt;
&lt;li&gt;styled-components&lt;/li&gt;
&lt;li&gt;webpack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Today, very few new React applications are built that way.&lt;/p&gt;
&lt;p&gt;Modern React development in 2026 is shaped by several major shifts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server-first rendering,&lt;/li&gt;
&lt;li&gt;TypeScript-first APIs,&lt;/li&gt;
&lt;li&gt;edge-ready runtimes,&lt;/li&gt;
&lt;li&gt;monorepo tooling,&lt;/li&gt;
&lt;li&gt;async React architecture,&lt;/li&gt;
&lt;li&gt;AI-assisted workflows,&lt;/li&gt;
&lt;li&gt;and significantly better developer experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting a new React project today can feel overwhelming because there are now dozens of excellent options in almost every category.&lt;/p&gt;
&lt;p&gt;Should you use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt; or &lt;a href=&quot;https://tanstack.com/start&quot;&gt;TanStack Start&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pmndrs/zustand&quot;&gt;Zustand&lt;/a&gt; or &lt;a href=&quot;https://github.com/pmndrs/jotai&quot;&gt;Jotai&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react-hook-form.com/&quot;&gt;React Hook Form&lt;/a&gt; or &lt;a href=&quot;https://tanstack.com/form&quot;&gt;TanStack Form&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot;&gt;shadcn/ui&lt;/a&gt; or &lt;a href=&quot;https://mantine.dev/&quot;&gt;Mantine&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; or &lt;a href=&quot;https://panda-css.com/&quot;&gt;Panda CSS&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vitest.dev/&quot;&gt;Vitest&lt;/a&gt; or &lt;a href=&quot;https://jestjs.io/&quot;&gt;Jest&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://better-auth.com/&quot;&gt;Better Auth&lt;/a&gt; or &lt;a href=&quot;https://authjs.dev/&quot;&gt;Auth.js&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answer depends heavily on what kind of product you are building.&lt;/p&gt;
&lt;p&gt;A SaaS dashboard has completely different requirements than:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a marketing site,&lt;/li&gt;
&lt;li&gt;a collaborative editor,&lt;/li&gt;
&lt;li&gt;an e-commerce storefront,&lt;/li&gt;
&lt;li&gt;a browser-based design tool,&lt;/li&gt;
&lt;li&gt;or a real-time analytics application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide breaks down the modern React stack category by category and explains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which tools became the new defaults,&lt;/li&gt;
&lt;li&gt;which libraries are losing momentum,&lt;/li&gt;
&lt;li&gt;which alternatives still make sense,&lt;/li&gt;
&lt;li&gt;and what tradeoffs come with every major decision.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not to create a universal ranking.&lt;/p&gt;
&lt;p&gt;Instead, the goal is to help developers understand how the React ecosystem actually looks in 2026.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React Ecosystem Trends in 2026&lt;/h2&gt;
&lt;p&gt;Before comparing libraries individually, it is important to understand the larger architectural trends shaping React development.&lt;/p&gt;
&lt;p&gt;The React ecosystem in 2026 is no longer centered around building purely client-side applications.&lt;/p&gt;
&lt;p&gt;Modern React applications increasingly focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server-first rendering,&lt;/li&gt;
&lt;li&gt;streaming interfaces,&lt;/li&gt;
&lt;li&gt;async workflows,&lt;/li&gt;
&lt;li&gt;edge execution,&lt;/li&gt;
&lt;li&gt;AI-assisted experiences,&lt;/li&gt;
&lt;li&gt;and shared type-safe contracts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Several ecosystem-wide changes influenced nearly every major library:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trend&lt;/th&gt;
&lt;th&gt;Impact on the Ecosystem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React Server Components&lt;/td&gt;
&lt;td&gt;Reduced client bundle sizes and moved data fetching closer to the server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript Everywhere&lt;/td&gt;
&lt;td&gt;Libraries increasingly design APIs around type inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-Assisted Development&lt;/td&gt;
&lt;td&gt;Tooling shifted toward faster iteration and code generation workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge Deployment&lt;/td&gt;
&lt;td&gt;Frameworks optimized for distributed execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monorepo Growth&lt;/td&gt;
&lt;td&gt;Shared packages and workspace tooling became standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utility-First Styling&lt;/td&gt;
&lt;td&gt;Tailwind and token-based systems became dominant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async UI Patterns&lt;/td&gt;
&lt;td&gt;Streaming and optimistic interfaces became common&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These shifts explain why many older React patterns no longer feel modern.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;heavy Redux architectures became less common,&lt;/li&gt;
&lt;li&gt;large runtime CSS-in-JS systems lost momentum,&lt;/li&gt;
&lt;li&gt;and traditional REST-heavy frontend architectures increasingly moved toward server actions and typed APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding these broader shifts makes it much easier to choose tools intentionally instead of simply following trends.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Visual Overview of a Modern React Stack&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;┌────────────────────────────────────────────┐
│                 Frontend                   │
├────────────────────────────────────────────┤
│ Next.js / TanStack Start / Remix           │
│ Tailwind CSS / shadcn/ui                   │
│ React Hook Form / TanStack Form            │
│ Zustand / Jotai                            │
│ TanStack Query                             │
└────────────────────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────┐
│              Shared Contracts              │
├────────────────────────────────────────────┤
│ TypeScript Types                           │
│ Zod Schemas                                │
│ API Contracts                              │
└────────────────────────────────────────────┘
                    │
                    ▼
┌────────────────────────────────────────────┐
│                 Backend                    │
├────────────────────────────────────────────┤
│ Node.js / NestJS / Hono                    │
│ Drizzle ORM / Prisma                       │
│ PostgreSQL                                 │
│ Better Auth                                │
└────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This architecture became extremely common because it balances:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;developer experience,&lt;/li&gt;
&lt;li&gt;scalability,&lt;/li&gt;
&lt;li&gt;maintainability,&lt;/li&gt;
&lt;li&gt;and type safety.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Frameworks and Meta Frameworks&lt;/h3&gt;
&lt;h4&gt;Next.js&lt;/h4&gt;
&lt;p&gt;Next.js is still the dominant React framework in 2026.&lt;/p&gt;
&lt;p&gt;Its biggest advantage is ecosystem gravity.&lt;/p&gt;
&lt;p&gt;You get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React Server Components,&lt;/li&gt;
&lt;li&gt;streaming,&lt;/li&gt;
&lt;li&gt;route handlers,&lt;/li&gt;
&lt;li&gt;server actions,&lt;/li&gt;
&lt;li&gt;edge support,&lt;/li&gt;
&lt;li&gt;image optimization,&lt;/li&gt;
&lt;li&gt;metadata handling,&lt;/li&gt;
&lt;li&gt;and deployment integration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The App Router has matured significantly compared to the early React 18 transition period.&lt;/p&gt;
&lt;p&gt;Modern Next.js applications usually combine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server components for data-heavy pages,&lt;/li&gt;
&lt;li&gt;client components only where interactivity is needed,&lt;/li&gt;
&lt;li&gt;and server actions for mutations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A typical modern Next.js stack often includes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default async function DashboardPage() {
  const projects = await getProjects();

  return (
    &amp;lt;DashboardLayout&amp;gt;
      &amp;lt;ProjectList projects={projects} /&amp;gt;
    &amp;lt;/DashboardLayout&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next.js is usually the safest choice for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SaaS products,&lt;/li&gt;
&lt;li&gt;content platforms,&lt;/li&gt;
&lt;li&gt;dashboards,&lt;/li&gt;
&lt;li&gt;e-commerce,&lt;/li&gt;
&lt;li&gt;and hybrid server/client applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;TanStack Start&lt;/h3&gt;
&lt;p&gt;TanStack Start became one of the most interesting alternatives to Next.js.&lt;/p&gt;
&lt;p&gt;Instead of focusing heavily on conventions, it focuses on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;type safety,&lt;/li&gt;
&lt;li&gt;route-based data loading,&lt;/li&gt;
&lt;li&gt;modern caching,&lt;/li&gt;
&lt;li&gt;and a highly composable architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Developers who already use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TanStack Router,&lt;/li&gt;
&lt;li&gt;TanStack Query,&lt;/li&gt;
&lt;li&gt;TanStack Form,&lt;/li&gt;
&lt;li&gt;or TanStack Table&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;usually feel very comfortable inside the TanStack ecosystem.&lt;/p&gt;
&lt;p&gt;TanStack Start especially shines in applications that require:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;advanced client-side routing,&lt;/li&gt;
&lt;li&gt;granular data fetching control,&lt;/li&gt;
&lt;li&gt;optimistic updates,&lt;/li&gt;
&lt;li&gt;and complex dashboard workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Remix&lt;/h3&gt;
&lt;p&gt;Remix still has one of the cleanest mental models for full-stack React.&lt;/p&gt;
&lt;p…</content:encoded></item><item><title>Friday Links #37 — JavaScript Trends &amp; Tools</title><link>https://jsdev.space/friday/friday-37/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-37/</guid><description>A curated roundup of the latest JavaScript releases, tools, AI updates, and performance benchmarks to keep developers ahead this week.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-37.png&quot; alt=&quot;Friday Links #37&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The JavaScript ecosystem doesn’t slow down—it evolves weekly. Friday Links #37 distills the most important updates across libraries, runtimes, tooling, and AI-driven development. From new releases pushing performance boundaries to emerging tools that simplify workflows, this issue highlights what’s worth your attention. Whether you’re building production systems or exploring new stacks, these curated picks help you stay informed without the noise.&lt;/p&gt;
&lt;h2&gt;🧠 Language &amp;amp; Runtime Updates&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Node.js 26 (Current) — almost here&lt;/strong&gt;
The upcoming release of Node.js 26 is expected to ship with V8 14.6 and enable the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal&quot;&gt;Temporal API&lt;/a&gt; by default—a major step toward handling dates natively without relying on third-party libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JavaScript (ES2025 → ES2026)&lt;/strong&gt;
The language continues evolving toward ES2026, with ongoing improvements to the standard library and deeper integration with AI-driven tooling and workflows.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://deno.com/blog/fresh-2.3&quot;&gt;🍋 Fresh 2.3&lt;/a&gt; — the Deno-native full-stack framework — introduces first-class WebSocket support, eliminates unnecessary JavaScript for pages that don’t need it, and makes the View Transitions API effortless with a single attribute in your views.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://piccalil.li/blog/the-end-of-responsive-images/&quot;&gt;The end of responsive images&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/articles/baseline-in-action-dialog-popover&quot;&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; and popover: Baseline layered UI patterns&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.joshwcomeau.com/animation/scroll-driven-animations/&quot;&gt;Scroll-Driven Animations&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.debugbear.com/blog/lazy-loading-performance&quot;&gt;How to Use Lazy Loading Without Hurting Web Performance&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nik.digital/posts/compositing-blending&quot;&gt;Compositing &amp;amp; Blending&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/css-multi-column-layout-wrapping-features/&quot;&gt;Looking at New CSS Multi-Column Layout Wrapping Features&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/recreating-apples-vision-pro-animation-in-css/&quot;&gt;Recreating Apple’s Vision Pro Animation in CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/constructable-stylesheets-and-adoptedstylesheets-one-parse-every-shadow-root/&quot;&gt;Constructable Stylesheets and adoptedStyleSheets: One Parse, Every Shadow Root&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nerdy.dev/CSS-recently-in-all-browsers&quot;&gt;CSS Recently In All Browsers&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2026/04/session-timeouts-accessibility-barrier-authentication-design/&quot;&gt;Session Timeouts: The Overlooked Accessibility Barrier In Authentication Design&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://saschb2b.com/blog/react-compiler-year-in-review&quot;&gt;The React Compiler at Eighteen Months: The Arc, the Debates, and What&apos;s Next&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://shadcnstudio.com/blog/migrate-from-radix-ui-to-base-ui&quot;&gt;Migrating from Radix UI to Base UI: Step-by-Step Guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://csswizardry.com/2026/04/font-family-doesnt-fall-back-the-way-you-think/&quot;&gt;font-family Doesn’t Fall Back the Way You Think&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.isquaredsoftware.com/presentations/2026-04-react-compiler-rendering/&quot;&gt;A Guide to React Compiler Rendering&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;TypeScript 7.0 Beta — gaining traction&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-beta/&quot;&gt;TypeScript 7&lt;/a&gt; is generating significant discussion. It introduces a native compiler written in Go, promising substantial performance gains and a redesigned architecture for large-scale projects.&lt;/p&gt;
&lt;p&gt;TypeScript 6 — a transitional release
TypeScript 6.0 acts as a bridge toward TS 7, bringing:&lt;/p&gt;
&lt;p&gt;Deprecation of legacy APIs
Updated DOM typings
Adoption of modern &lt;code&gt;import attributes&lt;/code&gt; (&lt;code&gt;with&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/perry.png&quot; alt=&quot;&amp;quot;Perry TypeScript Compiler&amp;quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.perryts.com/&quot;&gt;Perry&lt;/a&gt; is a cross-platform TypeScript compiler that compiles code directly into native executables, offering an alternative to traditional JavaScript runtimes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://openwarp.zerx.dev/en/&quot;&gt;OpenWarp&lt;/a&gt; - Bring any AI model into your terminal&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/view-transitions-mock&quot;&gt;View Transitions Mock&lt;/a&gt; — a non-visual polyfill for the View Transitions API — provides a JavaScript implementation of same-document transitions without the visual layer. You can write a single code path for all browsers: supported ones render transitions, while others fall back to a DOM swap, with the same promises and API behavior.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google-labs-code/design.md&quot;&gt;DESIGN.md&lt;/a&gt; is a proposed format for describing a product’s visual identity—colors, typography, spacing, and UI rules—in a structured, machine-readable way. It’s designed for AI agents and tooling, enabling them to generate consistent interfaces, designs, or code based on a single source of truth.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/open-circle/formisch&quot;&gt;Formisch&lt;/a&gt; — a schema-based, headless form library for JavaScript frameworks that handles form state and validation. It’s type-safe, fast by default, and keeps bundle size small thanks to its modular design.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://franktisellano.github.io/datatype/&quot;&gt;Datatype&lt;/a&gt; is a variable font that turns text into charts, providing a unique way to visualize data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/0xGF/boneyard&quot;&gt;Boneyard&lt;/a&gt; — generates pixel-perfect skeleton loading screens directly from your real UI, with no manual measurements or placeholder tuning.&lt;/p&gt;
&lt;p&gt;Works across frameworks including React, Preact, Vue, Svelte 5, Angular, and React Native.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/isaac-mason/crashcat&quot;&gt;crashcat&lt;/a&gt; — a JavaScript physics engine built for games, simulations, and creative web projects. It includes rigid body simulation, support for multiple shape types, continuous collision detection, and more for building dynamic, interactive experiences.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/harshankur/officeParser&quot;&gt;officeParser&lt;/a&gt; — a robust, strictly typed library for Node.js and the browser that parses office documents into a clean, hierarchical AST. It preserves rich metadata, text formatting, and supports embedded attachments.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/maximhq/bifrost&quot;&gt;Bifrost&lt;/a&gt; — a high-performance AI gateway that unifies access to 15+ providers (including OpenAI, Anthropic, Amazon Web Services, and Google) through a single OpenAI-compatible API.&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel-labs/portless&quot;&gt;Portless&lt;/a&gt; lets you replace port-based URLs with clean, stable local domains—so instead of &lt;code&gt;http://localhost:3000&lt;/code&gt;, you can use something like &lt;code&gt;https://myapp.localhost&lt;/code&gt;. It’s built on Node.js and now adds new features for Tailscale users.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/russellromney/honker&quot;&gt;Honker&lt;/a&gt; brings PostgreSQL-style NOTIFY/LISTEN semantics to SQLite—no daemon required. The honker-node package adds support for Node.js, making it easy to integrate event-driven messaging into your apps.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Secreto31126/whatsapp-api-js&quot;&gt;whatsapp-api-js&lt;/a&gt; — a lightweight, dependency-free library for interacting with the WhatsApp Cloud API, built for efficiency and full TypeScript support.&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pnpm.io/blog/releases/11.0&quot;&gt;pnpm 11.0 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/avajs/ava/releases/tag/v8.0.0&quot;&gt;AVA 8.0&lt;/a&gt; — the popular Node.js test runner is now fully ESM and introduces two new test modifiers: &lt;code&gt;test.skipIf()&lt;/code&gt; and &lt;code&gt;test.runIf()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/metafloor/bwip-js&quot;&gt;BWIP-JS 4.10&lt;/a&gt; — a pure JavaScript port of the origina…</content:encoded></item><item><title>Howto Use IMask.js for Input Masking in JavaScript Forms</title><link>https://jsdev.space/howto/imaskjs-input-masking/</link><guid isPermaLink="true">https://jsdev.space/howto/imaskjs-input-masking/</guid><description>Learn how to implement input masking for cards, dates, currency, and more using IMask.js with real-world examples and best practices.</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Form handling is one of the most underestimated parts of frontend development.&lt;/p&gt;
&lt;p&gt;It looks simple: just a few inputs, maybe a submit button. But in reality, forms are where most user frustration happens. Incorrect formats, unclear validation rules, and inconsistent input behavior all lead to errors, abandoned flows, and bad data.&lt;/p&gt;
&lt;p&gt;Input masking solves this at the source.&lt;/p&gt;
&lt;p&gt;Instead of reacting to bad input after submission, you guide the user while they type.&lt;/p&gt;
&lt;p&gt;In this article, we’ll go deep into IMask.js — a lightweight, flexible library that allows you to control input formatting in real time. This is not just a basic tutorial. You’ll learn patterns, edge cases, and production-ready practices.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Input Masking Is a Core UX Layer&lt;/h2&gt;
&lt;p&gt;Without masking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;users guess formats&lt;/li&gt;
&lt;li&gt;validation fails often&lt;/li&gt;
&lt;li&gt;error messages increase friction&lt;/li&gt;
&lt;li&gt;backend receives inconsistent data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With masking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input is guided instantly&lt;/li&gt;
&lt;li&gt;errors are prevented early&lt;/li&gt;
&lt;li&gt;formatting becomes predictable&lt;/li&gt;
&lt;li&gt;UX feels faster and more professional&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Masking is not just visual formatting — it&apos;s a &lt;strong&gt;data normalization layer at the UI level&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What Makes IMask.js Different&lt;/h2&gt;
&lt;p&gt;IMask.js stands out because it combines flexibility with performance.&lt;/p&gt;
&lt;p&gt;Key characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;no dependencies&lt;/li&gt;
&lt;li&gt;small bundle (~15kb gzipped)&lt;/li&gt;
&lt;li&gt;works with plain JS and frameworks&lt;/li&gt;
&lt;li&gt;supports multiple mask types&lt;/li&gt;
&lt;li&gt;allows custom logic via functions&lt;/li&gt;
&lt;li&gt;handles mobile input well&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unlike many libraries, IMask doesn’t lock you into one approach. It gives you primitives to build your own behavior.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pnpm add imask
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import MaskCore from &quot;imask&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Mental Model of IMask&lt;/h2&gt;
&lt;p&gt;Before writing code, understand how IMask works internally.&lt;/p&gt;
&lt;p&gt;There are three key concepts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Input Element&lt;/strong&gt; – the DOM node&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mask Configuration&lt;/strong&gt; – rules defining formatting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mask Instance&lt;/strong&gt; – runtime controller&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const inputNode = document.querySelector(&quot;#phone&quot;)

const maskController = MaskCore(inputNode, {
  mask: &quot;+{1} (000) 000-0000&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The instance acts as a bridge between raw input and formatted output.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Basic Example: Phone Input&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input id=&quot;contact-phone&quot; placeholder=&quot;+1 (___) ___-____&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;const phoneNode = document.querySelector(&quot;#contact-phone&quot;)

const phoneController = MaskCore(phoneNode, {
  mask: &quot;+{1} (000) 000-0000&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the user types, formatting is applied automatically.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Static Masks (Fixed Structure)&lt;/h2&gt;
&lt;p&gt;Use static masks when format is strictly defined.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const cardNode = document.querySelector(&quot;#card&quot;)

const cardController = MaskCore(cardNode, {
  mask: &quot;0000 0000 0000 0000&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Best for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;credit cards&lt;/li&gt;
&lt;li&gt;IDs&lt;/li&gt;
&lt;li&gt;formatted codes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Dynamic Masks (Multiple Formats)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const phoneDynamic = MaskCore(document.querySelector(&quot;#phone&quot;), {
  mask: [
    { mask: &quot;+{1} (000) 000-0000&quot; },
    { mask: &quot;+{44} 0000 000000&quot; }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IMask automatically selects the correct pattern based on input.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Number Mask (Currency &amp;amp; Prices)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const amountNode = document.querySelector(&quot;#amount&quot;)

const currencyController = MaskCore(amountNode, {
  mask: Number,
  min: 0,
  max: 1000000,
  thousandsSeparator: &quot;,&quot;,
  radix: &quot;.&quot;,
  precision: 2,
  prefix: &quot;$ &quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;decimal precision&lt;/li&gt;
&lt;li&gt;limits&lt;/li&gt;
&lt;li&gt;separators&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Date Mask&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const dateNode = document.querySelector(&quot;#date&quot;)

const dateController = MaskCore(dateNode, {
  mask: Date,
  pattern: &quot;yyyy-mm-dd&quot;,
  lazy: false
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents invalid formats like &lt;code&gt;2026-99-99&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Regex Mask&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const emailNode = document.querySelector(&quot;#email&quot;)

const emailController = MaskCore(emailNode, {
  mask: /^\S+@\S+\.\S+$/
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simple validation rules&lt;/li&gt;
&lt;li&gt;constrained input&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Function Mask (Full Control)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const evenNode = document.querySelector(&quot;#even&quot;)

const evenController = MaskCore(evenNode, {
  mask: (value) =&amp;gt; {
    const parsed = Number(value)
    return parsed % 2 === 0 ? value : value.slice(0, -1)
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach is powerful but should be used carefully.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Real-World Example: Credit Card Detection&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const ccNode = document.querySelector(&quot;#cc&quot;)

const ccController = MaskCore(ccNode, {
  mask: &quot;0000 0000 0000 0000&quot;
})

ccController.on(&quot;accept&quot;, () =&amp;gt; {
  const raw = ccController.unmaskedValue

  let type = &quot;unknown&quot;

  if (/^4/.test(raw)) type = &quot;visa&quot;
  else if (/^5[1-5]/.test(raw)) type = &quot;mastercard&quot;
  else if (/^3[47]/.test(raw)) type = &quot;amex&quot;

  console.log(type)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Events System&lt;/h2&gt;
&lt;p&gt;IMask exposes lifecycle hooks:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;maskController.on(&quot;accept&quot;, () =&amp;gt; {
  console.log(maskController.value)
})

maskController.on(&quot;complete&quot;, () =&amp;gt; {
  console.log(&quot;complete&quot;)
})

maskController.on(&quot;reject&quot;, (input) =&amp;gt; {
  console.log(&quot;rejected&quot;, input)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These allow integration with UI state, validation, and analytics.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Advanced Configuration&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;MaskCore(inputNode, {
  mask: &quot;0000-00-00&quot;,
  lazy: true,
  nullable: false,
  unmask: true,
  placeholderChar: &quot;_&quot;,
  prepare: (val) =&amp;gt; val.toUpperCase(),
  validate: (val) =&amp;gt; val.length === 10
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Important options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt; → allow partial input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unmask&lt;/code&gt; → return raw value&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prepare&lt;/code&gt; → transform input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;validate&lt;/code&gt; → enforce rules&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;React Integration&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { useEffect, useRef } from &quot;react&quot;
import MaskCore from &quot;imask&quot;

export function MaskedInput() {
  const nodeRef = useRef(null)

  useEffect(() =&amp;gt; {
    const instance = MaskCore(nodeRef.current, {
      mask: &quot;+{1} (000) 000-0000&quot;
    })

    return () =&amp;gt; instance.destroy()
  }, [])

  return &amp;lt;input ref={nodeRef} /&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Common Pitfalls&lt;/h2&gt;
&lt;h3&gt;1. Changing Value Directly&lt;/h3&gt;
&lt;p&gt;Wrong:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inputNode.value = &quot;123&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;maskController.value = &quot;123&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2. Autofill Conflicts&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input autocomplete=&quot;off&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or sync manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inputNode.addEventListener(&quot;change&quot;, () =&amp;gt; {
  maskController.updateValue()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3. Mobile Input Issues&lt;/h3&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lazy: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to reduce input jitter.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;keep masks readable&lt;/li&gt;
&lt;li&gt;avoid over-engineering regex&lt;/li&gt;
&lt;li&gt;always use unmaskedValue for backend&lt;/li&gt;
&lt;li&gt;keep commits atomic (if integrating with forms logic)&lt;/li&gt;
&lt;li&gt;test on mobile devices&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;When NOT to Use Masking&lt;/h2&gt;
&lt;p&gt;Avoid masking when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input is free text&lt;/li&gt;
&lt;li&gt;validation is business-driven (not format-driven)&lt;/li&gt;
&lt;li&gt;accessibility would suffer&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;IMask.js is not just a utility — it&apos;s a UX upgrade.&lt;/p&gt;
&lt;p&gt;It allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enforce structure at input level&lt;/li&gt;
&lt;li&gt;reduce validation complexity&lt;/li&gt;
&lt;li&gt;improve perceived performance&lt;/li&gt;
&lt;li&gt;create predictable user flows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once integrated properly, your forms stop being fragile.&lt;/p&gt;
&lt;p&gt;They become reliable systems.&lt;/p&gt;
&lt;p&gt;And in modern frontend development, that’s a huge advantage.&lt;/p…</content:encoded></item><item><title>Git for Beginners: Branches, Commits, and Your First Pull Request</title><link>https://jsdev.space/git-branches-commits-pull-request/</link><guid isPermaLink="true">https://jsdev.space/git-branches-commits-pull-request/</guid><description>A practical beginner’s guide to Git workflows. Learn how branches, commits, and pull requests work together in real development teams.</description><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Git is one of the most important tools in modern software development. Nearly every team relies on it to manage source code, collaborate, and track project history.&lt;/p&gt;
&lt;p&gt;Yet for beginners, Git often feels intimidating.&lt;/p&gt;
&lt;p&gt;Developers create branches with names like test123 or asd. Commits say things like “update”, “fix”, or even “...”. Pull requests contain dozens of unrelated files, temporary changes, and sometimes even accidental edits.&lt;/p&gt;
&lt;p&gt;And the worst part? Many developers don’t realize this is a problem.&lt;/p&gt;
&lt;p&gt;In a real team environment, sloppy Git usage quickly turns into chaos. Imagine trying to find the commit that broke the build when the history looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fix
update
changes
fix again
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good luck.&lt;/p&gt;
&lt;p&gt;Git is not just a storage system for code.
It is a communication tool between developers.&lt;/p&gt;
&lt;p&gt;In this guide, you&apos;ll learn how Git actually works and how to use it in a way that makes collaboration smooth and professional.&lt;/p&gt;
&lt;h2&gt;Git Is Not Magic — It’s Just a Graph&lt;/h2&gt;
&lt;p&gt;Before learning commands, it&apos;s important to understand what Git really stores.&lt;/p&gt;
&lt;p&gt;Git keeps project history as a graph of commits.&lt;/p&gt;
&lt;p&gt;Each commit represents a snapshot of the project at a specific moment in time.&lt;/p&gt;
&lt;p&gt;A commit contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file changes&lt;/li&gt;
&lt;li&gt;author information&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;li&gt;a unique hash&lt;/li&gt;
&lt;li&gt;a commit message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A branch is simply a pointer to a commit.&lt;/p&gt;
&lt;p&gt;When you create a new commit, the branch pointer moves forward.&lt;/p&gt;
&lt;p&gt;A simplified commit graph might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A --- B --- C (main)
       \
        D --- E (feature/auth)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./images/git-workflow.png&quot; alt=&quot;Git commit graph&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt; contains the stable project history&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feature/auth&lt;/code&gt; is a separate branch for developing a new feature&lt;/li&gt;
&lt;li&gt;once the feature is complete, it gets merged back into &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This isolation is what allows teams to work safely without interfering with each other.&lt;/p&gt;
&lt;h2&gt;Types of Git Branches&lt;/h2&gt;
&lt;p&gt;For small projects, teams often only use one main branch and a few temporary branches.&lt;/p&gt;
&lt;p&gt;But larger projects usually follow a structured workflow. One popular example is Git Flow.&lt;/p&gt;
&lt;p&gt;Git Flow defines several types of branches.&lt;/p&gt;
&lt;h3&gt;Main branches&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;main&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The production-ready branch.
Everything here should be stable and deployable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;develop&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The integration branch where new features are combined before release.&lt;/p&gt;
&lt;h3&gt;Temporary branches&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;feature/&lt;/strong&gt;*&lt;/p&gt;
&lt;p&gt;Branches used to develop new functionality.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feature/user-authentication
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;release/&lt;/strong&gt;*&lt;/p&gt;
&lt;p&gt;Used to prepare a new release.&lt;/p&gt;
&lt;p&gt;Tasks may include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bug fixes&lt;/li&gt;
&lt;li&gt;documentation updates&lt;/li&gt;
&lt;li&gt;version bumps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;hotfix/&lt;/strong&gt;*&lt;/p&gt;
&lt;p&gt;Urgent fixes applied directly to production code.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hotfix/security-patch
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A simpler workflow for beginners&lt;/h2&gt;
&lt;p&gt;Most teams teaching Git to newcomers use a simpler model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;main
  ├─ feature/add-login
  ├─ feature/create-api
  └─ fix/email-validation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each task gets its own branch.&lt;/p&gt;
&lt;p&gt;When the task is done, the branch is merged and deleted.&lt;/p&gt;
&lt;h2&gt;Creating Your First Branch&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/example/project.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into the project directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd project
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure your main branch is up to date:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git switch main
git pull origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new branch for your task:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git switch -c feature/add-user-model
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;creates a new branch&lt;/li&gt;
&lt;li&gt;switches to it immediately&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Older Git versions used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout -b feature/add-user-model
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both commands do the same thing.&lt;/p&gt;
&lt;h2&gt;Naming Branches Correctly&lt;/h2&gt;
&lt;p&gt;Branch naming conventions matter more than beginners think.&lt;/p&gt;
&lt;p&gt;Bad example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;branch1
new-feature
test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feature/add-user-service
bugfix/login-error
hotfix/token-expiration
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;General rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use lowercase&lt;/li&gt;
&lt;li&gt;avoid spaces&lt;/li&gt;
&lt;li&gt;use hyphens or slashes&lt;/li&gt;
&lt;li&gt;avoid non-ASCII characters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clear names help the entire team understand what the branch contains.&lt;/p&gt;
&lt;h2&gt;Making Meaningful Commits&lt;/h2&gt;
&lt;p&gt;After writing some code, the next step is creating commits.&lt;/p&gt;
&lt;p&gt;Imagine we add a simple JavaScript class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before committing, Git needs to know which files to include.&lt;/p&gt;
&lt;p&gt;Add the file to the staging area:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add src/main/project/User.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also add all changes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this is risky — temporary files might accidentally be committed.&lt;/p&gt;
&lt;p&gt;It is usually safer to add files explicitly.&lt;/p&gt;
&lt;h2&gt;Creating the commit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git commit -m &quot;Add User class&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works, but the message could be better.&lt;/p&gt;
&lt;p&gt;A good commit message answers two questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What changed?&lt;/li&gt;
&lt;li&gt;Why did it change?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Writing Good Commit Messages&lt;/h2&gt;
&lt;p&gt;A commonly used structure is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Short summary (max ~50 characters)

Detailed explanation of why the change was needed.
Explain important technical details if necessary.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Add User class for authentication module
Stores name and email fields used in login flow.
Email must be unique and will be validated by database
constraints in the next task.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Good commit messages help future developers understand the reasoning behind code changes.&lt;/p&gt;
&lt;p&gt;And often that future developer is you six months later.&lt;/p&gt;
&lt;h2&gt;Conventional Commits&lt;/h2&gt;
&lt;p&gt;Many teams use the Conventional Commits standard.&lt;/p&gt;
&lt;p&gt;It adds a prefix describing the type of change.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feat: add password reset endpoint
fix: prevent crash on empty email
docs: update installation guide
refactor: move validation logic to service
test: add login unit tests
chore: update dependencies
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Common prefixes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefix&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;feat&lt;/td&gt;
&lt;td&gt;new feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fix&lt;/td&gt;
&lt;td&gt;bug fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;documentation changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;style&lt;/td&gt;
&lt;td&gt;formatting only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;refactor&lt;/td&gt;
&lt;td&gt;code improvements without behavior change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;test&lt;/td&gt;
&lt;td&gt;new or updated tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chore&lt;/td&gt;
&lt;td&gt;tooling or configuration changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Example commit history:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feat: add User entity
fix: validate email format
refactor: extract email validation utility
test: add negative login tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes the project history easy to understand.&lt;/p&gt;
&lt;p&gt;It also enables tools that automatically generate changelogs.&lt;/p&gt;
&lt;h2&gt;Sending Code to GitHub&lt;/h2&gt;
&lt;p&gt;Once your work is committed, push it to the remote repository.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin feature/add-user-model
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the branch doesn&apos;t exist on the server yet, Git will create it.&lt;/p&gt;
&lt;p&gt;After pushing, go to GitHub (or GitLab) and you&apos;ll see a button:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create Pull Request&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;What Is a Pull Request?&lt;/h2&gt;
&lt;p&gt;A Pull Request (PR) is a request to merge your changes into another branch.&lt;/p&gt;
&lt;p&gt;Usually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feature branch → main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pull requests allow teammates to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;review code&lt;/li&gt;
&lt;li&gt;suggest improvements&lt;/li&gt;
&lt;li&gt;detect bugs early&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This …</content:encoded></item><item><title>Meet Effect TS: A New Way to Structure TypeScript Apps</title><link>https://jsdev.space/meet-effect-ts/</link><guid isPermaLink="true">https://jsdev.space/meet-effect-ts/</guid><description>Discover Effect TS, a powerful TypeScript framework for typed errors, dependency injection, structured concurrency, and composable backend architecture.</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The JavaScript ecosystem evolves extremely fast. New frameworks appear
constantly, each promising better performance, better developer
experience, or a simpler programming model.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://effect.website/&quot;&gt;Effect TS&lt;/a&gt; is different.&lt;/p&gt;
&lt;p&gt;It is not primarily a UI framework. It is not a backend framework in the
traditional sense either. Instead, Effect is an attempt to solve
something deeper: &lt;strong&gt;how complex TypeScript applications should be
structured&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Most real-world applications suffer from the same problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;asynchronous code becomes messy&lt;/li&gt;
&lt;li&gt;errors are unpredictable&lt;/li&gt;
&lt;li&gt;dependency injection becomes fragile&lt;/li&gt;
&lt;li&gt;background tasks are hard to manage&lt;/li&gt;
&lt;li&gt;libraries do not compose well together&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Effect TS approaches these problems from a new angle. Instead of solving
them separately, it introduces a unified programming model where
&lt;strong&gt;errors, dependencies, concurrency, and resources are all part of the
type system&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At first glance this model may look unusual. But once developers start
building real systems with it, many realize that it solves issues they
previously handled with dozens of separate libraries.&lt;/p&gt;
&lt;p&gt;This article introduces the core ideas behind Effect and explains why
more developers are experimenting with it in their TypeScript projects.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Developers Are Trying Effect&lt;/h2&gt;
&lt;p&gt;Effect has been gaining attention because it combines several powerful
ideas into a single framework.&lt;/p&gt;
&lt;p&gt;Below are ten practical reasons why developers start exploring it.&lt;/p&gt;
&lt;h2&gt;1. Errors Become Part of the Type System&lt;/h2&gt;
&lt;p&gt;Traditional JavaScript error handling is chaotic. A function can throw
anything: an Error instance, a string, or even an arbitrary object.&lt;/p&gt;
&lt;p&gt;In many projects this leads to code where the real failure scenarios are
not clearly defined.&lt;/p&gt;
&lt;p&gt;Effect changes this by making errors explicit.&lt;/p&gt;
&lt;p&gt;Instead of writing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function getUser(id: string): Promise&amp;lt;User&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you describe both success and failure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Effect&amp;lt;User, UserNotFoundError | NetworkError&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the compiler knows exactly which errors are possible. When those
errors are handled later, the type system verifies that no cases were
forgotten.&lt;/p&gt;
&lt;p&gt;This makes applications far more predictable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Dependency Injection Without Magic&lt;/h2&gt;
&lt;p&gt;Many backend frameworks use dependency injection containers based on
decorators or runtime reflection.&lt;/p&gt;
&lt;p&gt;While these systems work, they also hide dependencies behind runtime
behavior.&lt;/p&gt;
&lt;p&gt;Effect takes a different approach.&lt;/p&gt;
&lt;p&gt;Dependencies are represented directly in types. If a function requires a
service, the type signature reflects that requirement. The compiler
ensures the dependency is provided before the program runs.&lt;/p&gt;
&lt;p&gt;This eliminates an entire category of runtime errors.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Testing Becomes Much Simpler&lt;/h2&gt;
&lt;p&gt;Because dependencies are explicit, testing becomes easier.&lt;/p&gt;
&lt;p&gt;Instead of mocking modules globally or configuring complicated
containers, you can simply replace a service implementation with a test
version.&lt;/p&gt;
&lt;p&gt;This leads to tests that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;easier to understand&lt;/li&gt;
&lt;li&gt;easier to maintain&lt;/li&gt;
&lt;li&gt;less dependent on internal implementation details&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For large systems this improvement alone can be extremely valuable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Everything Is Designed to Compose&lt;/h2&gt;
&lt;p&gt;Effect encourages a compositional style of programming.&lt;/p&gt;
&lt;p&gt;Instead of deeply nested async code, logic is built as small pieces that
combine through pipelines.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pipe(
  fetchUsers(),
  Effect.map(filterActiveUsers),
  Effect.tap(logUserCount),
  Effect.flatMap(saveUsers)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each step transforms the previous result.&lt;/p&gt;
&lt;p&gt;This approach keeps complex workflows readable and easy to refactor.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Gradual Adoption Is Possible&lt;/h2&gt;
&lt;p&gt;One of the biggest advantages of Effect is that it does not require
rewriting an entire application.&lt;/p&gt;
&lt;p&gt;Existing Promise-based code can be wrapped using helper utilities.
Effects can also be executed as regular Promises.&lt;/p&gt;
&lt;p&gt;This means teams can adopt Effect incrementally. A project might start
with a few critical services and expand from there.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Concurrency Is Built Into the Model&lt;/h2&gt;
&lt;p&gt;JavaScript applications often require complex asynchronous workflows.&lt;/p&gt;
&lt;p&gt;Typical solutions involve combining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Promise.all&lt;/li&gt;
&lt;li&gt;retry libraries&lt;/li&gt;
&lt;li&gt;AbortController&lt;/li&gt;
&lt;li&gt;custom scheduling logic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Effect replaces this with a unified concurrency model.&lt;/p&gt;
&lt;p&gt;The framework provides tools for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;parallel execution&lt;/li&gt;
&lt;li&gt;task cancellation&lt;/li&gt;
&lt;li&gt;retries with backoff&lt;/li&gt;
&lt;li&gt;scheduling&lt;/li&gt;
&lt;li&gt;background tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All using the same abstractions.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. A Rich Built-In Toolkit&lt;/h2&gt;
&lt;p&gt;Effect is not just a runtime. It also includes a large ecosystem of
utilities.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Streams&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;PubSub channels&lt;/li&gt;
&lt;li&gt;Schedulers&lt;/li&gt;
&lt;li&gt;Date/time helpers&lt;/li&gt;
&lt;li&gt;transactional memory&lt;/li&gt;
&lt;li&gt;schema validation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of installing many unrelated libraries, developers often rely on
the tools provided by Effect itself.&lt;/p&gt;
&lt;p&gt;This reduces fragmentation in large codebases.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Observability Is Built In&lt;/h2&gt;
&lt;p&gt;Modern applications require visibility into their behavior.&lt;/p&gt;
&lt;p&gt;Logging, metrics, and tracing are essential for debugging and monitoring
production systems.&lt;/p&gt;
&lt;p&gt;Effect integrates observability directly into the framework and supports
standards such as OpenTelemetry.&lt;/p&gt;
&lt;p&gt;This makes it easier to instrument applications without complex
integrations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. A Growing Ecosystem&lt;/h2&gt;
&lt;p&gt;The core Effect team maintains several official packages that extend the
framework.&lt;/p&gt;
&lt;p&gt;Some examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;@effect/platform&lt;/strong&gt; -- platform utilities like HTTP and filesystem&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/sql&lt;/strong&gt; -- typed database access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/cli&lt;/strong&gt; -- command‑line application framework&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/rpc&lt;/strong&gt; -- typed remote procedure calls&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/cluster&lt;/strong&gt; -- primitives for distributed systems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@effect/ai&lt;/strong&gt; -- abstractions for language model integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because these packages follow the same design philosophy, they integrate
smoothly with each other.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Strong Type Feedback Helps AI Tools&lt;/h2&gt;
&lt;p&gt;AI‑assisted development tools rely heavily on compiler feedback.&lt;/p&gt;
&lt;p&gt;Effect&apos;s strong typing provides extremely clear signals when generated
code is incorrect.&lt;/p&gt;
&lt;p&gt;This often leads to faster corrections and fewer runtime bugs when using
AI coding assistants.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Core Idea: Effects&lt;/h2&gt;
&lt;p&gt;At the heart of the framework lies a single abstraction:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Effect&amp;lt;A, E, R&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This type represents a computation.&lt;/p&gt;
&lt;p&gt;The three parameters describe:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt; --- the successful result&lt;br /&gt;
&lt;strong&gt;E&lt;/strong&gt; --- the error type&lt;br /&gt;
&lt;strong&gt;R&lt;/strong&gt; --- required dependencies&lt;/p&gt;
&lt;p&gt;Compared to a Promise, which only represents success, Effect provides a
complete description of a computation.&lt;/p&gt;
&lt;p&gt;This allows the compiler to reason about behavior that would otherwise
only appear at runtime.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Creating Effects&lt;/h2&gt;
&lt;p&gt;The framework provides several helpers for constructing effects.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const value = Effect.succeed(42)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creating a failure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const error = Effect.fail(&quot;Unexpected error&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wrapping synchronous logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const random = Effect.sync(() =&amp;gt; Math.random())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These operations describe work rather than executing it immediatel…</content:encoded></item><item><title>Howto Deploy OpenClaw and Build Your Personal AI Second Brain</title><link>https://jsdev.space/howto/deploy-openclaw-second-brain/</link><guid isPermaLink="true">https://jsdev.space/howto/deploy-openclaw-second-brain/</guid><description>Learn how to deploy OpenClaw from scratch on Windows, macOS, or Linux and build a private AI assistant with memory, tool integrations, and automation.</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most AI assistants today are little more than chat interfaces. They
answer questions but cannot access your tools, remember your
preferences, or perform real work. &lt;a href=&quot;https://openclaw.ai/&quot;&gt;OpenClaw&lt;/a&gt; takes a different approach.&lt;/p&gt;
&lt;p&gt;Instead of functioning purely as a chatbot, OpenClaw acts as a
&lt;strong&gt;programmable cognitive partner&lt;/strong&gt;. It can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remember long‑term context&lt;/li&gt;
&lt;li&gt;connect to external tools&lt;/li&gt;
&lt;li&gt;automate workflows&lt;/li&gt;
&lt;li&gt;run entirely on infrastructure you control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide explains how to deploy OpenClaw from scratch and configure it
on &lt;strong&gt;Windows, macOS, or Linux&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;By the end of this tutorial you will have a working OpenClaw instance
running locally.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Use OpenClaw&lt;/h2&gt;
&lt;h3&gt;Private Deployment&lt;/h3&gt;
&lt;p&gt;All conversations and memory stay on your own machine or server.&lt;/p&gt;
&lt;h3&gt;Tool Integration&lt;/h3&gt;
&lt;p&gt;OpenClaw can interact with tools such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;calendars&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;automation workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Persistent Memory&lt;/h3&gt;
&lt;p&gt;OpenClaw includes both &lt;strong&gt;short‑term and long‑term memory&lt;/strong&gt;, allowing it
to remember:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user preferences&lt;/li&gt;
&lt;li&gt;project details&lt;/li&gt;
&lt;li&gt;conversation context&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Open Source&lt;/h3&gt;
&lt;p&gt;OpenClaw is released under the MIT license, meaning the entire system is
fully auditable and customizable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;System Requirements&lt;/h2&gt;
&lt;p&gt;Minimum environment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Node.js 18+
npm or pnpm
8 GB RAM recommended
2 GB disk space
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Supported platforms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;li&gt;Windows (PowerShell or WSL2)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Verify Your Environment&lt;/h2&gt;
&lt;h3&gt;macOS / Linux&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;node --version
npm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If Node is missing, install it from:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/&quot;&gt;Node.js&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Installation Method 1: One‑Step Installer&lt;/h2&gt;
&lt;h3&gt;macOS / Linux&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;curl -fsSL https://docs.openclaw.ai/install.sh | bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After installation open:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost:18789&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Windows (PowerShell)&lt;/h3&gt;
&lt;p&gt;Clone the repository and start the gateway manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/openclawai/openclaw.git
cd openclaw
npm install
npm run start:gateway
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Windows with WSL2 (Recommended)&lt;/h3&gt;
&lt;p&gt;Install WSL:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wsl --install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then inside WSL:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -fsSL https://docs.openclaw.ai/install.sh | bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Access the interface via:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost:18789&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Installation Method 2: Manual Installation&lt;/h2&gt;
&lt;h3&gt;Clone Repository&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/openclawai/openclaw.git
cd openclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Install Dependencies&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install -g pnpm
pnpm install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Configure Workspace&lt;/h3&gt;
&lt;p&gt;macOS / Linux&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export OPENCLAW_WORKSPACE=~/.openclaw/workspace
mkdir -p $OPENCLAW_WORKSPACE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows PowerShell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setx OPENCLAW_WORKSPACE &quot;%USERPROFILE%\.openclaw\workspace&quot;
mkdir %USERPROFILE%\.openclaw\workspace
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Copy Configuration&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cp config.example.yaml config.yaml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;p&gt;&lt;code&gt;copy config.example.yaml config.yaml&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Start Gateway&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pnpm start:gateway&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker Deployment&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    version: &quot;3.8&quot;

    services:
      openclaw:
        image: ghcr.io/openclaw/openclaw:latest
        container_name: openclaw
        ports:
          - &quot;18789:18789&quot;
          - &quot;18792:18792&quot;
        environment:
          - OPENCLAW_WORKSPACE=/workspace
          - NODE_ENV=production
        volumes:
          - ./workspace:/workspace
          - ./config.yaml:/app/config.yaml
          - ./logs:/var/log/openclaw
        restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the container:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;View logs:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose logs -f&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Configuring AI Models&lt;/h2&gt;
&lt;p&gt;Example configuration in &lt;code&gt;config.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    agents:
      defaults:
        model: &quot;siliconflow/deepseek-ai/DeepSeek-V3.2&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternative models:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openai/gpt-4
anthropic/claude-3-opus
google/gemini-2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Enabling Skills&lt;/h2&gt;
&lt;p&gt;Skills allow OpenClaw to interact with external services.&lt;/p&gt;
&lt;p&gt;Example configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    skills:
      enabled:
        - github
        - weather
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install additional skills:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;openclaw skills install github&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Creating Your First Custom Skill&lt;/h3&gt;
&lt;p&gt;Create directory:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir ~/.openclaw/workspace/skills/my-skill&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;SKILL.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## Custom Skill

### Description
Automation skill for internal APIs.

### Usage
/my-skill [arguments]

### Capabilities
- API access
- file reading
- workflow automation
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;h3&gt;Port Already In Use&lt;/h3&gt;
&lt;p&gt;macOS / Linux&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lsof -i :18789&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;p&gt;&lt;code&gt;netstat -ano | findstr 18789&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Kill process:&lt;/p&gt;
&lt;p&gt;Linux/macOS&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkill -f openclaw&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;p&gt;&lt;code&gt;taskkill /PID &amp;lt;pid&amp;gt; /F&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;OpenClaw is more than a chatbot. It is a programmable AI platform
capable of integrating with real workflows, remembering context, and
evolving through custom skills.&lt;/p&gt;
&lt;p&gt;Once deployed, it can become a powerful &lt;strong&gt;personal AI infrastructure
layer&lt;/strong&gt; that helps automate tasks, analyze code, and manage complex
workflows.&lt;/p&gt;
&lt;p&gt;For developers interested in building their own AI ecosystem, OpenClaw
provides a strong foundation for creating a true &lt;strong&gt;digital second
brain&lt;/strong&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Friday Links #36 — JavaScript Ecosystem Weekly</title><link>https://jsdev.space/friday/friday-36/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-36/</guid><description>A curated roundup of JavaScript ecosystem news: TypeScript, Node.js, Deno, AI developer tools, security discoveries, and new libraries from the past two weeks.</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-36.png&quot; alt=&quot;Friday Links #36&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The JavaScript ecosystem never slows down.&lt;br /&gt;
Over the past two weeks we’ve seen &lt;strong&gt;major updates across runtimes, frameworks, tooling, and security&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this issue of &lt;strong&gt;Friday Links&lt;/strong&gt;, we highlight the most interesting developments across the JavaScript world — from infrastructure changes and new tools to security research and ecosystem trends.&lt;/p&gt;
&lt;h2&gt;🧠 Ecosystem Highlights&lt;/h2&gt;
&lt;h3&gt;TypeScript 6 Prepares the Path to TS7&lt;/h3&gt;
&lt;p&gt;The TypeScript team &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-rc/&quot;&gt;released&lt;/a&gt; an early preview of &lt;strong&gt;TypeScript 6&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This release is mainly about &lt;strong&gt;internal changes preparing for the future Go-based compiler planned for TypeScript 7&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Key goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;faster compilation&lt;/li&gt;
&lt;li&gt;reduced memory usage&lt;/li&gt;
&lt;li&gt;better incremental builds&lt;/li&gt;
&lt;li&gt;improved large project performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Large monorepos could see &lt;strong&gt;dramatic speed improvements&lt;/strong&gt; once the Go compiler lands.&lt;/p&gt;
&lt;h3&gt;Deno 2.7 Improves Node Compatibility&lt;/h3&gt;
&lt;p&gt;The latest &lt;a href=&quot;https://deno.com/blog/v2.7&quot;&gt;&lt;strong&gt;Deno runtime release&lt;/strong&gt;&lt;/a&gt; continues improving Node compatibility.&lt;/p&gt;
&lt;p&gt;Highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;improved npm integration&lt;/li&gt;
&lt;li&gt;Node API compatibility&lt;/li&gt;
&lt;li&gt;Temporal API stabilization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const now = Temporal.Now.instant()
console.log(now.toString())
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.blog/ai-and-ml/generative-ai/under-the-hood-security-architecture-of-github-agentic-workflows/&quot;&gt;Under the hood: Security architecture of GitHub Agentic Workflows&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/beating-javascript-performance-limits-with-rust-and-n-api-building-a-faster-image-diff-tool&quot;&gt;Beating JavaScript Performance Limits With Rust and N-API: Building a Faster Image Diff Tool&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/the-different-ways-to-select-html-in-css/&quot;&gt;The Different Ways to Select &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; in CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/the-big-gotcha-of-anchor-positioning/&quot;&gt;The Big Gotcha of Anchor Positioning&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://neciudan.dev/cline-ci-got-compromised-here-is-how&quot;&gt;How to steal npm publish tokens by opening GitHub issues&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cardog.app/blog/vin-decoder-javascript&quot;&gt;How to Decode a VIN in JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.scottlogic.com/2026/03/09/noJS-3-flappy-bird.html&quot;&gt;Making a Flappy Bird clone using pure HTML and CSS, no JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ishchhabra.com/writing/pnpm-monorepo&quot;&gt;How to build a pnpm monorepo, the right way&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mux.com/blog/react-is-changing-the-game-for-streaming-apps-with-the-activity-component&quot;&gt;React is changing the game for streaming apps with the Activity component&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://patrickbrosset.com/articles/2026-03-09-using-css-animations-as-state-machines-to-remember-focus-and-hover-states-with-css-only/&quot;&gt;Using CSS animations as state machines to remember focus and hover states with CSS only&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.frankmtaylor.com/2026/03/05/you-dont-know-html-tables/&quot;&gt;You Don’t Know HTML Tables&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://reactdevelopment.substack.com/p/5-react-hooks-techniques-to-improve&quot;&gt;5 React Hooks Techniques to Improve Component Performance&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;h3&gt;Repomix — Turn Any Repo Into a Single AI-Readable File&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/repomix.png&quot; alt=&quot;Repomix&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadashy/repomix&quot;&gt;Repomix&lt;/a&gt; packs an entire repository into a single AI-friendly document.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tb5z035i/cursor-tg&quot;&gt;Cursor Cloud Telegram Connector&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://npmx.dev/&quot;&gt;npmx&lt;/a&gt; is an experimental tool designed to improve npm package exploration.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://litepacks.github.io/welyjs/&quot;&gt;Wely&lt;/a&gt; — Lightweight Web Component Framework&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vadimdemedes/ink&quot;&gt;Ink&lt;/a&gt; allows developers to build CLI tools using React components.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sojinantony01.github.io/react-cron-generator/&quot;&gt;Cron Expression Generator&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel/nft&quot;&gt;Node File Trace&lt;/a&gt; - determines exactly which files a Node application needs to run.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/privatenumber/minification-benchmarks&quot;&gt;JavaScript Minification Benchmarks&lt;/a&gt;: SWC Still Leads&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rv-grid.com/&quot;&gt;RevoGrid&lt;/a&gt; - High-Performance Data Grid Component&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/cosmiciron/vmprint&quot;&gt;VMPrint&lt;/a&gt; - A pure-JS, tiny typesetting engine with bit-perfect PDF output on everything—from Cloudflare Workers to the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/quantizor/markdown-to-jsx&quot;&gt;markdown-to-jsx&lt;/a&gt; -  A very fast and versatile markdown toolchain. Output to AST, React, React Native, SolidJS, Vue, HTML, and more!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sindresorhus/clipboardy&quot;&gt;clipboardy&lt;/a&gt; -  Access the system clipboard (copy/paste)&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/solidjs/solid/releases/tag/v2.0.0-beta.0&quot;&gt;Solid v2.0.0 Beta: The &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; Era Comes to an End&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After a long experimental phase, Solid 2.0 has released its first beta, introducing native asynchronous reactivity as a core feature of the framework.&lt;/p&gt;
&lt;p&gt;In this new model, reactive computations can directly return Promises or async iterables, and Solid’s reactive graph will automatically suspend and resume around those async operations. This removes much of the complexity developers previously had to manage when dealing with asynchronous state.&lt;/p&gt;
&lt;p&gt;One notable change is that &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; has been retired. For initial renders, it is now replaced by a simpler component called &lt;code&gt;&amp;lt;Loading&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build/blog/astro-6/&quot;&gt;Astro 6 is here!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/en/blog/release/v25.8.0&quot;&gt;Node.js 25.8.0 (Current)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://eslint.org/blog/2026/03/eslint-v10.0.3-released/&quot;&gt;ESLint v10.0.3 released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.emberjs.com/ember-released-6-11/&quot;&gt;Ember 6.11 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ionic.io/blog/announcing-ionic-framework-8-8&quot;&gt;Ionic Framework 8.8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/facebook/react-native/releases/tag/v0.85.0-rc.0&quot;&gt;React Native 0.85 RC.0&lt;/a&gt;, &lt;a href=&quot;https://github.com/pnpm/pnpm/releases/tag/v10.32.0&quot;&gt;pnpm 10.32&lt;/a&gt;, &lt;a href=&quot;https://github.com/jestjs/jest/releases/tag/v30.3.0&quot;&gt;Jest 30.3&lt;/a&gt;, &lt;a href=&quot;https://github.com/recharts/recharts/releases/tag/v3.8.0&quot;&gt;Recharts 3.8&lt;/a&gt;,
&lt;a href=&quot;https://github.com/openplayerjs/openplayerjs/releases/tag/v3.0.2&quot;&gt;OpenPlayer.js 3.0.2&lt;/a&gt;, &lt;a href=&quot;https://github.com/prisma/prisma/releases/tag/7.5.0&quot;&gt;Prisma 7.5&lt;/a&gt;, &lt;a href=&quot;https://github.com/sqliteai/sqlite-js&quot;&gt;SQLite JS 1.3&lt;/a&gt;, &lt;a href=&quot;https://github.com/staylor/react-helmet-async/pull/260&quot;&gt;React Helmet Async 3.0&lt;/a&gt;, &lt;a href=&quot;https://github.com/preactjs/preact/releases/tag/10.29.0&quot;&gt;Preact 10.29.0&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📺 Videos&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=IBTx5aGj-6U&quot;&gt;Build Your Own Video Sharing App – Loom Clone with Next.js and Mux JavaScript Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_TRV6fPUMJw&quot;&gt;You Can Just Ship Agents: Architecting for the Agentic Era | Dom Sipowicz, Vercel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=wvt5JNUXXLM&quot;&gt;The Future of TypeScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=IBTx5aGj-6U&quot;&gt;Build Your Own Video Sharing App – Loom Clone with Next.js and Mux JavaScript Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=abbeIUOCzmw&quot;&gt;Cloudflare just slop forked Next.js…&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Xn-gtHDsaPY&quot;&gt;7 new open source AI tools you need right now…&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.…</content:encoded></item><item><title>Valibot vs Zod: A Lightweight Validation Alternative</title><link>https://jsdev.space/valibot-vs-zod/</link><guid isPermaLink="true">https://jsdev.space/valibot-vs-zod/</guid><description>Compare Valibot and Zod in JavaScript and TypeScript apps. Explore bundle size, performance benchmarks, and real validation examples.</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Valibot vs Zod — Can a 1KB Validator Replace Zod?&lt;/h2&gt;
&lt;p&gt;Modern JavaScript applications rely heavily on external data sources. APIs, forms, query parameters, cookies, and configuration files all deliver data that your application must trust and process.&lt;/p&gt;
&lt;p&gt;But in practice, external data &lt;strong&gt;cannot be trusted&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Fields disappear. Types change. Backends evolve. A simple typo in a property name can break an entire page.&lt;/p&gt;
&lt;p&gt;Schema validation libraries solve this problem by adding a &lt;strong&gt;runtime validation layer&lt;/strong&gt; between your application and external data.&lt;/p&gt;
&lt;p&gt;Two libraries dominate this space today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zod.dev/&quot;&gt;Zod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://valibot.dev/&quot;&gt;Valibot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zod has been the standard for years. Valibot, however, is a newer library designed with a different philosophy: &lt;strong&gt;smaller bundles and faster validation&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Validation Libraries Matter&lt;/h2&gt;
&lt;p&gt;TypeScript provides compile‑time guarantees, but it cannot guarantee runtime correctness.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const response = await fetch(&quot;/api/user/1&quot;)
const user = await response.json()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Runtime data might not match expectations.&lt;/p&gt;
&lt;p&gt;Validation libraries ensure data integrity before it reaches business logic.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Validating API Responses with Valibot&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const ProductSchema = v.object({
  id: v.number(),
  title: v.string(),
  price: v.number(),
  rating: v.number(),
  images: v.array(v.string())
})

type Product = v.InferOutput&amp;lt;typeof ProductSchema&amp;gt;

export async function loadProduct(id:number):Promise&amp;lt;Product&amp;gt;{

  const res = await fetch(`https://dummyjson.com/products/${id}`)
  const data = await res.json()

  try{
    return v.parse(ProductSchema,data)
  }
  catch(error){
    console.error(&quot;Invalid API response&quot;,error)
    throw new Error(&quot;Product validation failed&quot;)
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Validating Form Input&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const RegistrationSchema = v.object({

  username:v.pipe(
    v.string(),
    v.minLength(3)
  ),

  email:v.pipe(
    v.string(),
    v.email()
  ),

  age:v.pipe(
    v.string(),
    v.digits(),
    v.transform(Number)
  ),

  password:v.pipe(
    v.string(),
    v.minLength(6)
  )

})

type RegistrationData = v.InferOutput&amp;lt;typeof RegistrationSchema&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Cross‑Field Validation&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const RegistrationSchema = v.pipe(

  v.object({

    email:v.pipe(
      v.string(&quot;Email must be text&quot;),
      v.email(&quot;Enter a valid email&quot;)
    ),

    age:v.pipe(
      v.string(),
      v.digits(&quot;Age must contain digits&quot;),
      v.transform(Number),
      v.minValue(18,&quot;You must be at least 18&quot;)
    ),

    password:v.pipe(
      v.string(),
      v.minLength(6,&quot;Password must contain at least 6 characters&quot;)
    ),

    confirmPassword:v.string()

  }),

  v.forward(
    v.partialCheck(
      [[&quot;password&quot;],[&quot;confirmPassword&quot;]],
      ({password,confirmPassword}) =&amp;gt; password === confirmPassword,
      &quot;Passwords do not match&quot;
    ),
    [&quot;confirmPassword&quot;]
  )

)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Code Comparison&lt;/h2&gt;
&lt;h3&gt;Valibot&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const BookSchema = v.object({
  title:v.string(),
  price:v.number()
})

v.parse(BookSchema,{title:&quot;JavaScript Guide&quot;,price:30})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Zod&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import {z} from &quot;zod&quot;

const BookSchema = z.object({
  title:z.string(),
  price:z.number()
})

BookSchema.parse({title:&quot;JavaScript Guide&quot;,price:30})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Benchmark Scripts&lt;/h2&gt;
&lt;h3&gt;Valibot&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import * as v from &quot;valibot&quot;

const schema = v.object({
 id:v.number(),
 name:v.string(),
 price:v.number(),
 rating:v.number(),
 tags:v.array(v.string())
})

const sample = {
 id:1,
 name:&quot;Laptop&quot;,
 price:1500,
 rating:4.8,
 tags:[&quot;tech&quot;,&quot;computer&quot;]
}

function benchmarkValibot(iterations:number){

 const start = performance.now()

 for(let i=0;i&amp;lt;iterations;i++){
  v.parse(schema,sample)
 }

 return performance.now() - start

}

console.log(&quot;Valibot:&quot;,benchmarkValibot(10000),&quot;ms&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Zod&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import {z} from &quot;zod&quot;

const schema = z.object({
 id:z.number(),
 name:z.string(),
 price:z.number(),
 rating:z.number(),
 tags:z.array(z.string())
})

const sample = {
 id:1,
 name:&quot;Laptop&quot;,
 price:1500,
 rating:4.8,
 tags:[&quot;tech&quot;,&quot;computer&quot;]
}

function benchmarkZod(iterations:number){

 const start = performance.now()

 for(let i=0;i&amp;lt;iterations;i++){
  schema.parse(sample)
 }

 return performance.now() - start

}

console.log(&quot;Zod:&quot;,benchmarkZod(10000),&quot;ms&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Benchmark Results&lt;/h2&gt;
&lt;h3&gt;Valid data&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;min (ms)&lt;/th&gt;
&lt;th&gt;max (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valibot&lt;/td&gt;
&lt;td&gt;26.58&lt;/td&gt;
&lt;td&gt;27.31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zod&lt;/td&gt;
&lt;td&gt;67.02&lt;/td&gt;
&lt;td&gt;70.25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Invalid data&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;min (ms)&lt;/th&gt;
&lt;th&gt;max (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valibot&lt;/td&gt;
&lt;td&gt;48.63&lt;/td&gt;
&lt;td&gt;54.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zod&lt;/td&gt;
&lt;td&gt;143.63&lt;/td&gt;
&lt;td&gt;147.79&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Bundle Size&lt;/h2&gt;
&lt;p&gt;Replacing Zod with Valibot reduced bundle size by &lt;strong&gt;~11.4 KB (gzip)&lt;/strong&gt; in a medium project.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Popularity&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Weekly downloads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valibot&lt;/td&gt;
&lt;td&gt;~1.2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zod&lt;/td&gt;
&lt;td&gt;~89.5M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Validation libraries protect applications from unreliable external data.&lt;/p&gt;
&lt;p&gt;Valibot proves that validation can be &lt;strong&gt;fast, lightweight, and developer‑friendly&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For new projects, Valibot is definitely worth considering.&lt;/p&gt;
</content:encoded></item><item><title>Why Blindly Using JSON.parse() Can Be Dangerous</title><link>https://jsdev.space/safe-json-parse-javascript/</link><guid isPermaLink="true">https://jsdev.space/safe-json-parse-javascript/</guid><description>Learn why blindly using JSON.parse() can introduce security risks like prototype pollution and DoS attacks, and how to safely parse JSON in modern JavaScript.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you write JavaScript for long enough—whether in the browser or on a Node.js server—you’ll almost certainly use &lt;code&gt;JSON.parse()&lt;/code&gt; countless times.&lt;/p&gt;
&lt;p&gt;It’s one of the most common APIs in the ecosystem.&lt;/p&gt;
&lt;p&gt;Developers rely on it for everything from reading configuration files to handling API requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const user = JSON.parse(localStorage.getItem(&quot;user&quot;))

const payload = JSON.parse(req.body.payload)

const config = JSON.parse(fs.readFileSync(&quot;config.json&quot;, &quot;utf-8&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s simple, fast, and built directly into the language.&lt;/p&gt;
&lt;p&gt;But here’s the problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many developers treat &lt;code&gt;JSON.parse()&lt;/code&gt; as a harmless utility, when in reality it can become a security risk if used carelessly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The function itself isn’t dangerous—but blindly parsing untrusted input can expose your application to attacks such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prototype pollution&lt;/li&gt;
&lt;li&gt;Denial of Service (DoS)&lt;/li&gt;
&lt;li&gt;Data injection attacks&lt;/li&gt;
&lt;li&gt;Unexpected runtime crashes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, we’ll explore why this happens and &lt;strong&gt;how to implement secure JSON parsing strategies in modern JavaScript applications&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Understanding What JSON.parse() Actually Does&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; converts a JSON string into a JavaScript object.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const json = &apos;{&quot;name&quot;: &quot;Alice&quot;, &quot;role&quot;: &quot;admin&quot;}&apos;

const user = JSON.parse(json)

console.log(user.name)
// Alice
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function faithfully reconstructs the &lt;strong&gt;entire object structure&lt;/strong&gt; contained in the JSON string.&lt;/p&gt;
&lt;p&gt;That includes every property name provided by the input.&lt;/p&gt;
&lt;p&gt;And that’s where the problems start.&lt;/p&gt;
&lt;h2&gt;Security Risk #1: Prototype Pollution&lt;/h2&gt;
&lt;p&gt;One of the most common vulnerabilities related to unsafe JSON handling is prototype pollution.&lt;/p&gt;
&lt;p&gt;Prototype pollution occurs when attackers manipulate an object&apos;s prototype to inject properties into every object in the application.&lt;/p&gt;
&lt;p&gt;If your code merges or copies parsed objects without validation, an attacker can inject special keys like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__proto__
constructor
prototype
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These keys can modify global object behavior.&lt;/p&gt;
&lt;h3&gt;Example of a Prototype Pollution Payload&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const payload = &apos;{&quot;__proto__&quot;: {&quot;isAdmin&quot;: true}}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine parsing it and merging the object into your system:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = JSON.parse(payload)

Object.assign({}, data)

console.log({}.isAdmin)
// true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suddenly every object in your application has a new property.&lt;/p&gt;
&lt;p&gt;This can lead to severe consequences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication bypass&lt;/li&gt;
&lt;li&gt;Privilege escalation&lt;/li&gt;
&lt;li&gt;Corrupted application logic&lt;/li&gt;
&lt;li&gt;Security policy violations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many well-known vulnerabilities in JavaScript ecosystems have involved prototype pollution through unvalidated input.&lt;/p&gt;
&lt;h3&gt;Why This Happens&lt;/h3&gt;
&lt;p&gt;JavaScript objects inherit from &lt;code&gt;Object.prototype&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If attackers manage to inject properties into the prototype chain, the impact becomes global.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log({}.toString)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works because every object inherits from &lt;code&gt;Object.prototype&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If attackers add properties there, every object in your system changes behavior.&lt;/p&gt;
&lt;h2&gt;Security Risk #2: Denial of Service (DoS)&lt;/h2&gt;
&lt;p&gt;Another common attack vector is resource exhaustion.&lt;/p&gt;
&lt;p&gt;Attackers can send extremely large or deeply nested JSON strings that consume huge amounts of CPU or memory during parsing.&lt;/p&gt;
&lt;h3&gt;Example: Deep Nesting Attack&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const evil = &apos;{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{&quot;a&quot;:{}}}}}}}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even small nested objects can cause heavy recursion.&lt;/p&gt;
&lt;p&gt;With enough depth, parsing can freeze the event loop.&lt;/p&gt;
&lt;h3&gt;Example: Huge Array Attack&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const payload = `[${&quot;1,&quot;.repeat(10000000)}1]`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A JSON string representing ten million elements can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allocate gigabytes of memory&lt;/li&gt;
&lt;li&gt;Freeze Node.js for seconds&lt;/li&gt;
&lt;li&gt;Crash the process with an Out Of Memory error&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For public APIs, this effectively becomes a remote kill switch.&lt;/p&gt;
&lt;h2&gt;Secure JSON Parsing Strategy&lt;/h2&gt;
&lt;p&gt;The safest approach combines three defensive layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Input size limits&lt;/li&gt;
&lt;li&gt;Key filtering&lt;/li&gt;
&lt;li&gt;Schema validation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s look at each one.&lt;/p&gt;
&lt;h3&gt;Step 1: Limit Input Size&lt;/h3&gt;
&lt;p&gt;Before parsing JSON, always check the size of the input.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function safeParse(jsonString, maxSize = 100 * 1024) {
  if (typeof jsonString !== &quot;string&quot;) {
    throw new Error(&quot;Invalid input type&quot;)
  }

  if (jsonString.length &amp;gt; maxSize) {
    throw new Error(&quot;JSON payload too large&quot;)
  }

  return JSON.parse(jsonString)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents attackers from sending extremely large payloads.&lt;/p&gt;
&lt;p&gt;In production APIs, limits are often set between &lt;strong&gt;50KB and 1MB&lt;/strong&gt;, depending on use case.&lt;/p&gt;
&lt;h3&gt;Step 2: Block Dangerous Keys&lt;/h3&gt;
&lt;p&gt;JavaScript allows a reviver function when parsing JSON.&lt;/p&gt;
&lt;p&gt;This lets you inspect every property during parsing.&lt;/p&gt;
&lt;p&gt;You can use it to reject suspicious keys.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function secureParse(jsonString) {
  return JSON.parse(jsonString, (key, value) =&amp;gt; {
    if (key === &quot;__proto__&quot; || key === &quot;constructor&quot; || key === &quot;prototype&quot;) {
      throw new Error(&quot;Forbidden key detected&quot;)
    }

    return value
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple check prevents prototype pollution payloads.&lt;/p&gt;
&lt;h3&gt;Step 3 (Best Practice): Runtime Schema Validation&lt;/h3&gt;
&lt;p&gt;Modern JavaScript applications rarely trust raw data.&lt;/p&gt;
&lt;p&gt;Instead, they validate data structures using schema validation libraries like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zod&lt;/li&gt;
&lt;li&gt;Joi&lt;/li&gt;
&lt;li&gt;Yup&lt;/li&gt;
&lt;li&gt;Ajv&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These tools ensure the parsed data matches an expected structure.&lt;/p&gt;
&lt;h3&gt;Example Using Zod&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  isAdmin: z.boolean().optional()
})

function parseUser(json) {
  const parsed = secureParse(json)

  return UserSchema.parse(parsed)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic runtime validation&lt;/li&gt;
&lt;li&gt;Clear error messages&lt;/li&gt;
&lt;li&gt;Type inference for TypeScript&lt;/li&gt;
&lt;li&gt;Protection against unexpected fields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Schema validation is considered modern best practice in production systems.&lt;/p&gt;
&lt;h2&gt;Additional Risks in Node.js&lt;/h2&gt;
&lt;p&gt;Server environments are especially vulnerable because JSON often comes from external sources.&lt;/p&gt;
&lt;p&gt;Common risky inputs include:&lt;/p&gt;
&lt;h3&gt;HTTP Requests&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;req.body
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Uploaded Configuration Files&lt;/h3&gt;
&lt;p&gt;Users may upload JSON configuration files.&lt;/p&gt;
&lt;p&gt;If parsed blindly, they can trigger crashes.&lt;/p&gt;
&lt;h3&gt;Database Data&lt;/h3&gt;
&lt;p&gt;Stored JSON may contain unexpected fields or corrupted structures.&lt;/p&gt;
&lt;h3&gt;Third-Party Webhooks&lt;/h3&gt;
&lt;p&gt;External services send JSON payloads that should never be trusted blindly.&lt;/p&gt;
&lt;h2&gt;Recommended Defensive Checklist&lt;/h2&gt;
&lt;p&gt;Before parsing JSON in production systems, verify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data source is trusted&lt;/li&gt;
&lt;li&gt;The payload size is limited&lt;/li&gt;
&lt;li&gt;Dangerous keys are filtered&lt;/li&gt;
&lt;li&gt;The structure matches a schema&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This layered approach dramatically reduces attack surfaces.&lt;/p&gt;
&lt;h2&gt;Is &lt;code&gt;JSON.parse()&lt;/code&gt; Actually Unsafe?&lt;/h2&gt;
&lt;p&gt;Not exactly.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; itself does not execute JavaScript code.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;eval()&lt;/code&gt;, it only reconstructs objects.&lt;/p&gt;
&lt;p&gt;However:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using it without validating input is equivalent to trusting user data blindly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And trusting user input is one of the most common sources of security vulnerabilities.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; is not a bad API.&lt;/p&gt;
&lt;p&gt;But using it without safeguards is like handling a powerful tool without protective equipm…</content:encoded></item><item><title>Tailwind CSS v4 vs MUI, Ant Design &amp; Styled Components</title><link>https://jsdev.space/tailwind-v4-vs-mui-antd-styled-components/</link><guid isPermaLink="true">https://jsdev.space/tailwind-v4-vs-mui-antd-styled-components/</guid><description>Architectural comparison of Tailwind CSS v4, MUI, Ant Design, and Styled Components—runtime costs, design tokens, dead CSS, theming, and scaling tradeoffs.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2026, picking a styling approach is less about taste and more about architecture.&lt;/p&gt;
&lt;p&gt;Teams don’t just “choose CSS.” They choose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how design decisions are encoded and shared,&lt;/li&gt;
&lt;li&gt;how fast UI can change without regressions,&lt;/li&gt;
&lt;li&gt;how much runtime work happens on every render,&lt;/li&gt;
&lt;li&gt;how quickly a codebase accumulates styling debt,&lt;/li&gt;
&lt;li&gt;and how portable the solution is across frameworks and products.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article compares four popular directions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS v4&lt;/a&gt; (utility-first styling engine)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mui.com/&quot;&gt;MUI&lt;/a&gt; and &lt;a href=&quot;https://ant.design/&quot;&gt;Ant Design&lt;/a&gt; (component libraries with strong opinions)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://styled-components.com/&quot;&gt;Styled Components&lt;/a&gt; (CSS-in-JS)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn’t to crown a universal winner. It’s to help you pick the right tool &lt;em&gt;for your system’s constraints&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;1. These tools are different “layers” of the stack&lt;/h2&gt;
&lt;p&gt;The biggest mistake is to compare them as if they are interchangeable.&lt;/p&gt;
&lt;h3&gt;MUI and Ant Design: component systems (not just styling)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/mui-ant-design.png&quot; alt=&quot;MUI and Ant Design&quot; /&gt;&lt;/p&gt;
&lt;p&gt;MUI and Ant Design are ready-made UI component ecosystems. You’re not just getting colors and spacing. You’re getting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;behavior,&lt;/li&gt;
&lt;li&gt;accessibility (A11Y) defaults,&lt;/li&gt;
&lt;li&gt;keyboard navigation,&lt;/li&gt;
&lt;li&gt;focus management in modals,&lt;/li&gt;
&lt;li&gt;edge-case handling that took years to harden.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need to ship an internal admin tool quickly, this matters.&lt;/p&gt;
&lt;p&gt;But you pay for it in other ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They impose a &lt;strong&gt;DOM structure&lt;/strong&gt; (wrappers, internal elements, slots).&lt;/li&gt;
&lt;li&gt;They impose an &lt;strong&gt;opinionated design language&lt;/strong&gt; (Material for MUI, Ant’s system for Ant).&lt;/li&gt;
&lt;li&gt;Customization often becomes an ongoing negotiation between your design system and theirs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your product design is “close enough” to their defaults, you move fast.
If your goal is “pixel-perfect Figma that looks like none of the defaults,” you can end up fighting the library.&lt;/p&gt;
&lt;h3&gt;Tailwind v4: styling engine (not a component library)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/tailwind.png&quot; alt=&quot;Tailwind v4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tailwind doesn’t know what a Date Picker is. It doesn’t ship one. It gives you low-level primitives to build your own.&lt;/p&gt;
&lt;p&gt;Tailwind’s advantage is control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you decide markup structure,&lt;/li&gt;
&lt;li&gt;you decide constraints,&lt;/li&gt;
&lt;li&gt;you can match a custom design system without constantly overriding component internals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tailwind v4 also reduced setup friction: you can start with a single CSS import (&lt;code&gt;@import &quot;tailwindcss&quot;;&lt;/code&gt;) instead of the older directive-heavy setup.&lt;/p&gt;
&lt;h3&gt;Styled Components: component-local styling with runtime generation&lt;/h3&gt;
&lt;p&gt;Styled Components sits between the two extremes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not a component library,&lt;/li&gt;
&lt;li&gt;but tightly binds CSS to components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is productive—until the project grows and you start asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which styles are still used?&lt;/li&gt;
&lt;li&gt;Which are dead?&lt;/li&gt;
&lt;li&gt;What’s the real cost of style generation during renders?&lt;/li&gt;
&lt;li&gt;How do we maintain consistent tokens without duplicating logic across many components?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tailwind and CSS-in-JS solve different problems. If you treat them as the same category, you’ll make architectural tradeoffs accidentall&lt;/p&gt;
&lt;h2&gt;2. Performance: static CSS vs runtime style generation&lt;/h2&gt;
&lt;p&gt;Performance isn’t just about “Tailwind is fast.” It’s about where work happens.&lt;/p&gt;
&lt;h3&gt;Runtime overhead (especially visible at scale)&lt;/h3&gt;
&lt;p&gt;CSS-in-JS solutions and some UI libraries generate and manage styles at runtime. That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extra JavaScript execution,&lt;/li&gt;
&lt;li&gt;extra style insertion/management,&lt;/li&gt;
&lt;li&gt;more work during render-heavy scenarios (large tables, infinite lists, fast state updates).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tailwind’s output is &lt;strong&gt;static CSS&lt;/strong&gt;. The browser receives it once and applies it like normal styles.&lt;/p&gt;
&lt;p&gt;That’s not automatically “always faster,” but it changes the performance profile in a way that becomes very noticeable in high-frequency UI updates.&lt;/p&gt;
&lt;h3&gt;Build performance in Tailwind v4&lt;/h3&gt;
&lt;p&gt;Tailwind v4 shipped with a “ground-up” performance rewrite and a faster build engine—reported as up to ~5× faster full builds and 100× faster incremental builds in the official announcements and coverage.&lt;/p&gt;
&lt;p&gt;That matters in real teams because build speed directly affects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iteration loops,&lt;/li&gt;
&lt;li&gt;design token tweaking,&lt;/li&gt;
&lt;li&gt;refactoring velocity,&lt;/li&gt;
&lt;li&gt;developer experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. “Self-cleaning CSS”: why dead styles don’t stick around (as much)&lt;/h2&gt;
&lt;p&gt;Large projects die by a thousand cuts—most of them are “small”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unused classes,&lt;/li&gt;
&lt;li&gt;abandoned component variants,&lt;/li&gt;
&lt;li&gt;theme overrides nobody remembers,&lt;/li&gt;
&lt;li&gt;CSS specificity battles that accumulate over years.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The typical CSS-in-JS failure mode&lt;/h3&gt;
&lt;p&gt;CSS-in-JS keeps styles “close” to components, which is great for local reasoning.&lt;/p&gt;
&lt;p&gt;But when components get removed or rewritten, styles can still remain in the bundle depending on usage patterns, exports, barrel files, and how build tooling marks code as reachable.&lt;/p&gt;
&lt;p&gt;Teams end up with a question they can’t easily answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Is this style still used anywhere?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Tailwind’s approach: generate only what’s referenced&lt;/h3&gt;
&lt;p&gt;Tailwind’s workflow is built around scanning your source for class usage and generating the necessary CSS for those classes. That “only what you used” approach is central to Tailwind’s output model.&lt;/p&gt;
&lt;p&gt;So when you delete a component, the classes that disappear from your markup will stop being included in the compiled CSS.&lt;/p&gt;
&lt;p&gt;This is one reason Tailwind projects often avoid the classic “CSS landfill” problem.&lt;/p&gt;
&lt;h3&gt;What about helpers like &lt;code&gt;tailwind-variants&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;Utilities like &lt;a href=&quot;https://www.tailwind-variants.org/&quot;&gt;&lt;code&gt;tailwind-variants&lt;/code&gt;&lt;/a&gt; (or any class-composition helper) don’t change the fundamental logic: Tailwind still cares about class strings it can discover. The important architectural point is that Tailwind’s output is driven by class usage, not “a stylesheet someone forgot to delete.”&lt;/p&gt;
&lt;h2&gt;4. Design tokens: from Figma to code without losing alignment&lt;/h2&gt;
&lt;p&gt;Modern UI teams don’t want “colors in CSS.” They want tokens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--color-brand&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--spacing-section&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--radius-card&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--font-sans&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tailwind v4’s CSS-first theming&lt;/h3&gt;
&lt;p&gt;Tailwind v4 introduced a CSS-first workflow where theme variables are defined using the @theme directive. Tailwind describes these as special CSS variables that influence which utilities exist.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@import &quot;tailwindcss&quot;;

@theme {
  --color-brand: oklch(0.55 0.22 260);
  --color-brand-hover: oklch(0.45 0.22 260);
  --font-sans: &quot;Inter&quot;, system-ui;
  --spacing-section: 4rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once defined, Tailwind can expose utilities based on those variables (for example, color-related tokens become usable via utility classes). The docs describe how theme variables map into usable styling in your project.&lt;/p&gt;
&lt;p&gt;Then UI usage becomes simple and consistent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function BrandButton() {
  return (
    &amp;lt;button className=&quot;bg-brand hover:bg-brand-hover text-white px-4 py-2 rounded&quot;&amp;gt;
      Button
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This token-driven flow aligns well with Figma Variables / token pipelines because it creates a single “source of truth” layer that can be shared across apps.&lt;/p&gt;
&lt;h3&gt;How this compares to MUI / Styled Components theming&lt;/h3&gt;
&lt;p&gt;MUI theming is powerful, but it typically lives in JavaScript objects and flows through providers and library AP…</content:encoded></item><item><title>JavaScript Note: ToggleEvent.source and dialog.closedBy</title><link>https://jsdev.space/toggleevent-source-dialog-closedby/</link><guid isPermaLink="true">https://jsdev.space/toggleevent-source-dialog-closedby/</guid><description>Practical guide to ToggleEvent.source and dialog.closedBy for cleaner dialog and popover behavior in modern browsers.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The modern web platform continues evolving with small but powerful
improvements. Two recent additions --- &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent/source&quot;&gt;&lt;strong&gt;&lt;code&gt;ToggleEvent.source&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; and the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby&quot;&gt;&lt;strong&gt;&lt;code&gt;closedby&lt;/code&gt; attribute for &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; --- make working with dialogs
and popovers significantly easier.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleEvent.source&lt;/code&gt; allows developers to determine &lt;strong&gt;which element
triggered a popover or dialog visibility change&lt;/strong&gt;, while &lt;code&gt;closedby&lt;/code&gt;
allows you to &lt;strong&gt;declare how a dialog can be closed&lt;/strong&gt; without writing
extra JavaScript.&lt;/p&gt;
&lt;p&gt;These additions move the web platform further toward &lt;strong&gt;declarative UI
behavior&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;ToggleEvent.source&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;source&lt;/code&gt; property&lt;/strong&gt; of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent&quot;&gt;&lt;code&gt;ToggleEvent&lt;/code&gt;&lt;/a&gt; interface is a
&lt;strong&gt;read‑only reference to the element that triggered the toggle event&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In simple terms, it tells you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Which element opened or closed a popover or dialog.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The property returns an instance of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element&quot;&gt;&lt;strong&gt;&lt;code&gt;Element&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If the visibility change was triggered &lt;strong&gt;programmatically&lt;/strong&gt;, the value
will be &lt;strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Browser support&lt;/h3&gt;
&lt;p&gt;Most modern browsers &lt;a href=&quot;https://caniuse.com/wf-toggleevent-source&quot;&gt;already support&lt;/a&gt; the property. Safari currently
exposes it behind an experimental flag.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/ToggleEvent-source.png&quot; alt=&quot;Browser support for ToggleEvent.source&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Elements that can trigger popovers&lt;/h3&gt;
&lt;p&gt;A popover can be triggered by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button commandfor=&quot;...&quot;&amp;gt;&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#commandfor&quot;&gt;commandfor attribute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button popovertarget=&quot;...&quot;&amp;gt;&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#popovertarget&quot;&gt;popovertarget attribute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;input type=&quot;button&quot; popovertarget=&quot;...&quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Elements that can behave as popovers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Any element with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover&quot;&gt;&lt;code&gt;popover&lt;/code&gt;&lt;/a&gt; attribute&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Consider a dialog with multiple buttons that close it.&lt;/p&gt;
&lt;p&gt;We want to determine &lt;strong&gt;which button closed the dialog&lt;/strong&gt; and display the
result.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;button commandfor=&quot;my-dialog&quot; command=&quot;show-modal&quot;&amp;gt;
    Show modal dialog
  &amp;lt;/button&amp;gt;

  &amp;lt;dialog id=&quot;my-dialog&quot;&amp;gt;
    &amp;lt;h3&amp;gt;Do you like modern Web APIs?&amp;lt;/h3&amp;gt;

    &amp;lt;div style=&quot;display:flex; gap:10px&quot;&amp;gt;
      &amp;lt;button commandfor=&quot;my-dialog&quot; command=&quot;close&quot; data-answer=&quot;yes&quot;&amp;gt;
        Yes
      &amp;lt;/button&amp;gt;

      &amp;lt;button commandfor=&quot;my-dialog&quot; command=&quot;close&quot; data-answer=&quot;sure&quot;&amp;gt;
        Sure
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/dialog&amp;gt;

  &amp;lt;p&amp;gt;No answer yet&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we listen for the &lt;code&gt;toggle&lt;/code&gt; event.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const paragraph = document.querySelector(&quot;p&quot;);

document.querySelector(&quot;dialog&quot;).addEventListener(&quot;toggle&quot;, (event) =&amp;gt; {

  if (!(event.source instanceof HTMLButtonElement)) return;

  const { answer } = event.source.dataset;

  if (answer) {
    paragraph.textContent = `Answer: ${answer}`;
  }

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;ToggleEvent.source&lt;/code&gt;, determining the trigger element becomes
trivial.&lt;/p&gt;
&lt;h2&gt;&amp;lt;Codepen id=&quot;019cbfd7-7afe-776d-afbe-461c3c60fb50&quot; /&amp;gt;&lt;/h2&gt;
&lt;h2&gt;HTMLDialogElement.closedBy&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/closedBy&quot;&gt;&lt;strong&gt;&lt;code&gt;closedby&lt;/code&gt; attribute&lt;/strong&gt;&lt;/a&gt; defines &lt;strong&gt;which user actions are allowed to
close a dialog&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Previously, developers often needed custom JavaScript logic to control
this behavior. Now it can be defined directly in HTML.&lt;/p&gt;
&lt;p&gt;This attribute is supported by all major browsers (Safari currently ships it behind an experimental flag).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/dialog-closedby.png&quot; alt=&quot;Browser support for dialog.closedBy&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Supported closing actions&lt;/h3&gt;
&lt;p&gt;Dialogs can be closed by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clicking outside the dialog on the overlay (light dismiss).&lt;/li&gt;
&lt;li&gt;Platform actions such as pressing &lt;strong&gt;Esc&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A developer-defined action such as a button calling
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close&quot;&gt;&lt;code&gt;dialog.close()&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;Attribute values&lt;/h2&gt;
&lt;h3&gt;any&lt;/h3&gt;
&lt;p&gt;The dialog can be closed using &lt;strong&gt;all methods&lt;/strong&gt; above.&lt;/p&gt;
&lt;h3&gt;closerequest&lt;/h3&gt;
&lt;p&gt;The dialog can be closed by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pressing &lt;strong&gt;Esc&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;developer-defined logic&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;none&lt;/h3&gt;
&lt;p&gt;The dialog can only be closed &lt;strong&gt;programmatically or by explicit UI
controls&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Default behavior&lt;/h2&gt;
&lt;p&gt;The default value depends on how the dialog is opened.&lt;/p&gt;
&lt;p&gt;If opened using: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal&quot;&gt;&lt;code&gt;showModal()&lt;/code&gt;&lt;/a&gt;, the default becomes: &lt;code&gt;closerequest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Otherwise the default is: &lt;code&gt;none&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Why this matters&lt;/h3&gt;
&lt;p&gt;Before &lt;code&gt;closedby&lt;/code&gt;, implementing overlay click closing required JavaScript logic, such as custom hooks (&lt;a href=&quot;https://jsdev.space/10-custom-react-hooks/&quot;&gt;&lt;code&gt;useClickOutside&lt;/code&gt;&lt;/a&gt; in React).&lt;/p&gt;
&lt;p&gt;Now this behavior can be defined purely declaratively.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Example with multiple dialogs&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;demo&quot;&amp;gt;
  &amp;lt;button class=&quot;open&quot; commandfor=&quot;dlg-any&quot; command=&quot;show-modal&quot;&amp;gt;
    Open dialog (closedby=&quot;any&quot;)
  &amp;lt;/button&amp;gt;

  &amp;lt;!-- 1) closedby=&quot;any&quot; --&amp;gt;
  &amp;lt;dialog id=&quot;dlg-any&quot; closedby=&quot;any&quot;&amp;gt;
    &amp;lt;div class=&quot;dialog&quot;&amp;gt;
      &amp;lt;header class=&quot;header&quot;&amp;gt;
        &amp;lt;h3&amp;gt;Dialog A — closedby=&quot;any&quot;&amp;lt;/h3&amp;gt;
        &amp;lt;button class=&quot;icon&quot; commandfor=&quot;dlg-any&quot; command=&quot;close&quot; aria-label=&quot;Close&quot;&amp;gt;
          &amp;amp;times;
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;p&amp;gt;
        This dialog can be closed by clicking the backdrop, pressing Esc, or using a Close button.
      &amp;lt;/p&amp;gt;

      &amp;lt;footer class=&quot;footer&quot;&amp;gt;
        &amp;lt;button class=&quot;cancel&quot; commandfor=&quot;dlg-any&quot; command=&quot;close&quot;&amp;gt;Close&amp;lt;/button&amp;gt;

        &amp;lt;button class=&quot;confirm&quot; commandfor=&quot;dlg-closerequest&quot; command=&quot;show-modal&quot;&amp;gt;
          Open dialog B
        &amp;lt;/button&amp;gt;
      &amp;lt;/footer&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/dialog&amp;gt;

  &amp;lt;!-- 2) closedby=&quot;closerequest&quot; --&amp;gt;
  &amp;lt;dialog id=&quot;dlg-closerequest&quot; closedby=&quot;closerequest&quot;&amp;gt;
    &amp;lt;div class=&quot;dialog&quot;&amp;gt;
      &amp;lt;header class=&quot;header&quot;&amp;gt;
        &amp;lt;h3&amp;gt;Dialog B — closedby=&quot;closerequest&quot;&amp;lt;/h3&amp;gt;
        &amp;lt;button class=&quot;icon&quot; commandfor=&quot;dlg-closerequest&quot; command=&quot;close&quot; aria-label=&quot;Close&quot;&amp;gt;
          &amp;amp;times;
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;p&amp;gt;
        This dialog closes via Esc or explicit controls. Clicking the backdrop should NOT close it.
      &amp;lt;/p&amp;gt;

      &amp;lt;footer class=&quot;footer&quot;&amp;gt;
        &amp;lt;button class=&quot;cancel&quot; commandfor=&quot;dlg-closerequest&quot; command=&quot;close&quot;&amp;gt;Close&amp;lt;/button&amp;gt;

        &amp;lt;button class=&quot;confirm&quot; commandfor=&quot;dlg-none&quot; command=&quot;show-modal&quot;&amp;gt;
          Open dialog C
        &amp;lt;/button&amp;gt;
      &amp;lt;/footer&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/dialog&amp;gt;

  &amp;lt;!-- 3) closedby=&quot;none&quot; --&amp;gt;
  &amp;lt;dialog id=&quot;dlg-none&quot; closedby=&quot;none&quot;&amp;gt;
    &amp;lt;div class=&quot;dialog&quot;&amp;gt;
…</content:encoded></item><item><title>How to Build an LRU Cache from Scratch in JavaScript</title><link>https://jsdev.space/howto/lru-cache-javascript/</link><guid isPermaLink="true">https://jsdev.space/howto/lru-cache-javascript/</guid><description>Learn how to implement an LRU cache in JavaScript from scratch using a Map and doubly linked list with O(1) operations. Includes step-by-step explanations and code.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Caching is a fundamental optimization technique used across nearly every modern software system. Whether you&apos;re building a web API, a database engine, or a frontend application, caching can dramatically improve performance by storing frequently accessed data in memory.&lt;/p&gt;
&lt;p&gt;One of the most common cache eviction strategies is LRU (Least Recently Used). You’ll encounter it in systems like Redis, operating systems, browser caches, and backend frameworks. It also appears frequently in technical interviews because implementing it correctly requires understanding both data structures and time complexity.&lt;/p&gt;
&lt;p&gt;Many developers initially find LRU confusing, especially because interview questions usually require O(1) time complexity for both retrieval and insertion operations. However, once you understand the right combination of data structures, the implementation becomes surprisingly elegant.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll build an LRU cache from scratch, following a clear progression:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Understanding what LRU actually means&lt;/li&gt;
&lt;li&gt;Defining the required operations&lt;/li&gt;
&lt;li&gt;Choosing the right data structures&lt;/li&gt;
&lt;li&gt;Implementing the cache step by step&lt;/li&gt;
&lt;li&gt;Testing the implementation&lt;/li&gt;
&lt;li&gt;Avoiding common mistakes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end, you&apos;ll have a production-ready LRU cache implementation in JavaScript.&lt;/p&gt;
&lt;h2&gt;What Is an LRU Cache?&lt;/h2&gt;
&lt;p&gt;LRU stands for &lt;strong&gt;Least Recently Used&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When the cache reaches its capacity, remove the item that hasn&apos;t been used for the longest time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every time an item is accessed or inserted, it becomes the &lt;strong&gt;most recently used&lt;/strong&gt; entry.&lt;/p&gt;
&lt;h2&gt;A Simple Real-World Analogy&lt;/h2&gt;
&lt;p&gt;Imagine a small bookshelf that can only hold three books.&lt;/p&gt;
&lt;p&gt;Capacity = 3&lt;/p&gt;
&lt;h3&gt;Step 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Add Algorithms
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Add Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Algorithms, Java]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3&lt;/h3&gt;
&lt;p&gt;Add Python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Algorithms, Java, Python]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The shelf is now full.&lt;/p&gt;
&lt;h3&gt;Step 4 – Access &quot;Algorithms&quot;&lt;/h3&gt;
&lt;p&gt;Because you used it recently, it moves to the end:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Java, Python, Algorithms]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 5 – Add &quot;JavaScript&quot;&lt;/h3&gt;
&lt;p&gt;Since the shelf is full, remove the least recently used item (&lt;strong&gt;Java&lt;/strong&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Python, Algorithms, JavaScript]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This behavior is exactly how an &lt;strong&gt;LRU cache works&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Core Requirements of an LRU Cache&lt;/h2&gt;
&lt;p&gt;In most implementations (and interviews), the cache supports two operations.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Required Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get(key)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return the value associated with the key, or &lt;code&gt;-1&lt;/code&gt; if not found. Also mark it as recently used.&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put(key, value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert or update a value. If capacity is exceeded, remove the least recently used item.&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;strong&gt;O(1)&lt;/strong&gt; requirement is critical.&lt;/p&gt;
&lt;p&gt;If operations become &lt;strong&gt;O(n)&lt;/strong&gt;, the implementation is no longer optimal.&lt;/p&gt;
&lt;h2&gt;Why Arrays Won’t Work&lt;/h2&gt;
&lt;p&gt;A naive solution might store items in an array.&lt;/p&gt;
&lt;p&gt;But arrays cause problems:&lt;/p&gt;
&lt;h3&gt;Finding an item&lt;/h3&gt;
&lt;p&gt;Requires scanning → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Moving an item&lt;/h3&gt;
&lt;p&gt;Requires shifting elements → &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Removing the least used item&lt;/h3&gt;
&lt;p&gt;Also &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This violates the &lt;strong&gt;O(1)&lt;/strong&gt; requirement.&lt;/p&gt;
&lt;p&gt;So we need a better approach.&lt;/p&gt;
&lt;h2&gt;The Key Insight: Combine Two Data Structures&lt;/h2&gt;
&lt;p&gt;To achieve constant time operations, we combine:&lt;/p&gt;
&lt;h3&gt;1. Hash Map (Map)&lt;/h3&gt;
&lt;p&gt;Provides:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;key → node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instant lookup&lt;/li&gt;
&lt;li&gt;O(1) access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But a Map cannot track usage order.&lt;/p&gt;
&lt;h3&gt;2. Doubly Linked List&lt;/h3&gt;
&lt;p&gt;Maintains item order:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Least used  ←→  Most used
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fast removal&lt;/li&gt;
&lt;li&gt;Fast insertion&lt;/li&gt;
&lt;li&gt;Fast reordering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Operations on linked lists can be &lt;strong&gt;O(1)&lt;/strong&gt; if you already have the node reference.&lt;/p&gt;
&lt;h2&gt;Final Architecture&lt;/h2&gt;
&lt;p&gt;We combine both structures:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Map:
key → linked list node

Doubly Linked List:
[Least Recently Used ... Most Recently Used]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Rules&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Head = least recently used&lt;/li&gt;
&lt;li&gt;Tail = most recently used&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 1: Create a Linked List Node&lt;/h3&gt;
&lt;p&gt;Each node stores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key&lt;/li&gt;
&lt;li&gt;value&lt;/li&gt;
&lt;li&gt;previous pointer&lt;/li&gt;
&lt;li&gt;next pointer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class CacheNode {
  constructor(key, value) {
    this.key = key
    this.value = value
    this.prev = null
    this.next = null
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Important detail:&lt;/p&gt;
&lt;p&gt;We store the key inside the node so we can remove it from the Map during eviction.&lt;/p&gt;
&lt;h3&gt;Step 2: Implement a Doubly Linked List&lt;/h3&gt;
&lt;p&gt;We’ll use sentinel (dummy) nodes for easier boundary handling.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class DoublyLinkedList {
  constructor() {
    this.head = new CacheNode(null, null)
    this.tail = new CacheNode(null, null)

    this.head.next = this.tail
    this.tail.prev = this.head

    this.length = 0
  }

  remove(node) {
    node.prev.next = node.next
    node.next.prev = node.prev

    node.prev = null
    node.next = null

    this.length--
  }

  addToEnd(node) {
    const prevLast = this.tail.prev

    prevLast.next = node
    node.prev = prevLast
    node.next = this.tail
    this.tail.prev = node

    this.length++
  }

  removeFirst() {
    if (this.length === 0) return null

    const first = this.head.next
    this.remove(first)

    return first
  }

  size() {
    return this.length
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: Build the LRU Cache&lt;/h3&gt;
&lt;p&gt;Now we combine the Map and linked list.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class LRUCache {
  constructor(capacity) {
    if (capacity &amp;lt;= 0) {
      throw new Error(&quot;Capacity must be greater than zero&quot;)
    }

    this.capacity = capacity
    this.cache = new Map()
    this.list = new DoublyLinkedList()
  }

  get(key) {
    if (!this.cache.has(key)) {
      return -1
    }

    const node = this.cache.get(key)

    this.list.remove(node)
    this.list.addToEnd(node)

    return node.value
  }

  put(key, value) {
    if (this.cache.has(key)) {
      const node = this.cache.get(key)

      node.value = value

      this.list.remove(node)
      this.list.addToEnd(node)

      return
    }

    if (this.list.size() === this.capacity) {
      const removed = this.list.removeFirst()

      if (removed) {
        this.cache.delete(removed.key)
      }
    }

    const newNode = new CacheNode(key, value)

    this.list.addToEnd(newNode)
    this.cache.set(key, newNode)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Testing the Implementation&lt;/h3&gt;
&lt;p&gt;Let&apos;s verify the behavior.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function testLRUCache() {
  const cache = new LRUCache(3)

  cache.put(&quot;Algorithms&quot;, 1)
  cache.put(&quot;Java&quot;, 2)
  cache.put(&quot;Python&quot;, 3)

  console.log(cache.get(&quot;Algorithms&quot;))
  // 1

  cache.put(&quot;JavaScript&quot;, 4)

  console.log(cache.get(&quot;Java&quot;))
  // -1 (evicted)

  console.log(cache.get(&quot;JavaScript&quot;))
  // 4

  cache.put(&quot;Python&quot;, 33)

  console.log(cache.get(&quot;Python&quot;))
  // 33
}

testLRUCache()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the output matches expectations, the cache works correctly.&lt;/p&gt;
&lt;h2&gt;Common Mistakes Developers Make&lt;/h2&gt;
&lt;h3&gt;1. Forgetting to store the key in nodes&lt;/h3&gt;
&lt;p&gt;Without the key, eviction cannot remove the Map entry.&lt;/p&gt;
&lt;h3&gt;2. Using a singly linked list&lt;/h3&gt;
&lt;p&gt;Deleting nodes becomes &lt;strong&gt;O(n)&lt;/strong&gt; because the previous node must be searched.&lt;/p&gt;
&lt;h3&gt;3. Not using sentinel nodes&lt;/h3&gt;
&lt;p&gt;This causes many edge cases when inserting or deleting.&lt;/p&gt;
&lt;h3&gt;4. Forgetting to update the size counter&lt;/h3&gt;
&lt;p&gt;This breaks capacity checks.&lt;/p&gt;
&lt;h3&gt;5. Updating value without updating position&lt;/h3&gt;
&lt;p&gt;Accessing an item sho…</content:encoded></item><item><title>How to Build a Safe JSON Parser for Node.js APIs</title><link>https://jsdev.space/howto/safe-json-parser-nodejs/</link><guid isPermaLink="true">https://jsdev.space/howto/safe-json-parser-nodejs/</guid><description>Learn how to build a safe JSON parser wrapper for Node.js APIs with payload limits, schema validation, dangerous key filtering, and production-ready error handling.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Parsing JSON in Node.js looks trivial at first glance.&lt;/p&gt;
&lt;p&gt;Most developers start with something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = JSON.parse(rawBody)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And for a while, that feels good enough.&lt;/p&gt;
&lt;p&gt;Then real traffic arrives.&lt;/p&gt;
&lt;p&gt;A client sends malformed JSON. A webhook includes fields you did not expect. A bot posts a giant payload. A malicious user tries to poison object prototypes. Suddenly, one innocent-looking &lt;code&gt;JSON.parse()&lt;/code&gt; call turns into a source of crashes, broken validation, or security issues.&lt;/p&gt;
&lt;p&gt;That is why production APIs should not parse JSON blindly.&lt;/p&gt;
&lt;p&gt;A safer approach is to build a JSON parser wrapper that handles the boring but critical work for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reject oversized payloads&lt;/li&gt;
&lt;li&gt;block dangerous keys&lt;/li&gt;
&lt;li&gt;validate structure at runtime&lt;/li&gt;
&lt;li&gt;return consistent errors&lt;/li&gt;
&lt;li&gt;integrate cleanly with Express, Fastify, Hono, or custom Node.js servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, we will build a reusable safe JSON parser wrapper for Node.js APIs, explain the design decisions behind it, and finish with production-ready examples you can adapt to your own backend.&lt;/p&gt;
&lt;h2&gt;Why a Wrapper Is Better Than Plain JSON.parse()&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; is not bad. It is fast, built-in, and perfectly fine when the input is trusted.&lt;/p&gt;
&lt;p&gt;The real problem is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In APIs, input is often untrusted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;request bodies&lt;/li&gt;
&lt;li&gt;webhooks&lt;/li&gt;
&lt;li&gt;uploaded JSON files&lt;/li&gt;
&lt;li&gt;queue messages&lt;/li&gt;
&lt;li&gt;Redis payloads&lt;/li&gt;
&lt;li&gt;database JSON blobs&lt;/li&gt;
&lt;li&gt;third-party callbacks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When raw input comes from outside your system, parsing should do more than just convert a string into an object.&lt;/p&gt;
&lt;p&gt;A proper parser layer should answer these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the payload too large?&lt;/li&gt;
&lt;li&gt;Is the JSON valid?&lt;/li&gt;
&lt;li&gt;Does it contain suspicious keys?&lt;/li&gt;
&lt;li&gt;Does it match the shape the API expects?&lt;/li&gt;
&lt;li&gt;Can the application return a clean error instead of crashing?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is exactly what a wrapper solves.&lt;/p&gt;
&lt;h2&gt;What a Safe JSON Parser Should Do&lt;/h2&gt;
&lt;p&gt;A strong implementation should provide five protections.&lt;/p&gt;
&lt;h3&gt;1. Type checks&lt;/h3&gt;
&lt;p&gt;Only strings or buffers should be accepted as raw JSON input.&lt;/p&gt;
&lt;h3&gt;2. Payload size limits&lt;/h3&gt;
&lt;p&gt;This helps reduce DoS risk and prevents huge request bodies from consuming memory.&lt;/p&gt;
&lt;h3&gt;3. Dangerous key filtering&lt;/h3&gt;
&lt;p&gt;Keys like &lt;code&gt;__proto__&lt;/code&gt;, &lt;code&gt;constructor&lt;/code&gt;, and &lt;code&gt;prototype&lt;/code&gt; can become a problem in unsafe merge flows.&lt;/p&gt;
&lt;h3&gt;4. Runtime validation&lt;/h3&gt;
&lt;p&gt;Even valid JSON is not necessarily valid application data.&lt;/p&gt;
&lt;h3&gt;5. Consistent error handling&lt;/h3&gt;
&lt;p&gt;The API should return predictable, human-readable errors instead of generic crashes.&lt;/p&gt;
&lt;h2&gt;The Core Design&lt;/h2&gt;
&lt;p&gt;We will build the wrapper in layers.&lt;/p&gt;
&lt;p&gt;First, a small custom error class.
Then a secure parser with size checks and key filtering.
Then a schema-aware parser using Zod.
Finally, an Express middleware example.&lt;/p&gt;
&lt;p&gt;This approach keeps the code modular and easy to test.&lt;/p&gt;
&lt;h3&gt;Step 1: Create Custom Error Types&lt;/h3&gt;
&lt;p&gt;A parser wrapper becomes much easier to integrate when it throws structured errors instead of generic &lt;code&gt;Error&lt;/code&gt; objects.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export class JsonParseError extends Error {
  public readonly statusCode: number
  public readonly code: string
  public readonly details?: unknown

  constructor(message: string, options?: {
    statusCode?: number
    code?: string
    details?: unknown
  }) {
    super(message)
    this.name = &quot;JsonParseError&quot;
    this.statusCode = options?.statusCode ?? 400
    this.code = options?.code ?? &quot;INVALID_JSON&quot;
    this.details = options?.details
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the rest of the application useful metadata:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;statusCode&lt;/code&gt; for HTTP responses&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code&lt;/code&gt; for machine-readable error handling&lt;/li&gt;
&lt;li&gt;&lt;code&gt;details&lt;/code&gt; for debugging or validation reports&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 2: Define Safe Defaults&lt;/h3&gt;
&lt;p&gt;Now define the parser configuration.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export type SafeJsonParserOptions = {
  maxBytes?: number
  forbiddenKeys?: string[]
}

const DEFAULT_FORBIDDEN_KEYS = [&quot;__proto__&quot;, &quot;constructor&quot;, &quot;prototype&quot;]

const DEFAULT_OPTIONS: Required&amp;lt;SafeJsonParserOptions&amp;gt; = {
  maxBytes: 100 * 1024,
  forbiddenKeys: DEFAULT_FORBIDDEN_KEYS,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These defaults are intentionally conservative.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;100KB&lt;/code&gt; default is reasonable for many APIs, but you can raise or lower it depending on your use case.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;small JSON commands: 10KB&lt;/li&gt;
&lt;li&gt;standard API forms: 50KB to 200KB&lt;/li&gt;
&lt;li&gt;large content payloads: 500KB to 1MB&lt;/li&gt;
&lt;li&gt;file uploads: do not parse with raw &lt;code&gt;JSON.parse()&lt;/code&gt; at all&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 3: Normalize Raw Input&lt;/h3&gt;
&lt;p&gt;In Node.js, raw request data may arrive as a string or a buffer.&lt;/p&gt;
&lt;p&gt;A wrapper should handle both cleanly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function normalizeInput(raw: string | Buffer): string {
  if (typeof raw === &quot;string&quot;) {
    return raw
  }

  if (Buffer.isBuffer(raw)) {
    return raw.toString(&quot;utf8&quot;)
  }

  throw new JsonParseError(&quot;Unsupported input type&quot;, {
    code: &quot;INVALID_INPUT_TYPE&quot;,
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents accidental misuse and keeps the main parser logic simpler.&lt;/p&gt;
&lt;h3&gt;Step 4: Enforce a Payload Size Limit&lt;/h3&gt;
&lt;p&gt;Before calling &lt;code&gt;JSON.parse()&lt;/code&gt;, check size first.&lt;/p&gt;
&lt;p&gt;This is one of the easiest and most effective hardening steps.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function assertPayloadSize(raw: string, maxBytes: number): void {
  const byteLength = Buffer.byteLength(raw, &quot;utf8&quot;)

  if (byteLength &amp;gt; maxBytes) {
    throw new JsonParseError(
      `JSON payload exceeds the ${maxBytes} byte limit`,
      {
        statusCode: 413,
        code: &quot;PAYLOAD_TOO_LARGE&quot;,
        details: { maxBytes, actualBytes: byteLength },
      }
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why check bytes instead of &lt;code&gt;string.length&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Because network payloads are measured in bytes, not characters. UTF-8 characters can take more than one byte, so &lt;code&gt;Buffer.byteLength()&lt;/code&gt; is the safer choice.&lt;/p&gt;
&lt;h3&gt;Step 5: Reject Dangerous Keys During Parsing&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;reviver&lt;/code&gt; argument of &lt;code&gt;JSON.parse()&lt;/code&gt; lets you inspect every key-value pair during parsing.&lt;/p&gt;
&lt;p&gt;That makes it a good place to block suspicious keys.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function createSecureReviver(forbiddenKeys: string[]) {
  const blocked = new Set(forbiddenKeys)

  return function secureReviver(key: string, value: unknown) {
    if (blocked.has(key)) {
      throw new JsonParseError(`Forbidden key found in JSON: ${key}`, {
        code: &quot;FORBIDDEN_JSON_KEY&quot;,
        details: { key },
      })
    }

    return value
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does not magically solve all object safety issues across your application, but it reduces risk significantly when dealing with untrusted payloads.&lt;/p&gt;
&lt;h3&gt;Step 6: Build the Base Safe Parser&lt;/h3&gt;
&lt;p&gt;Now combine the pieces into one reusable function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { JsonParseError } from &quot;./json-parse-error&quot;

export type SafeJsonParserOptions = {
  maxBytes?: number
  forbiddenKeys?: string[]
}

const DEFAULT_FORBIDDEN_KEYS = [&quot;__proto__&quot;, &quot;constructor&quot;, &quot;prototype&quot;]

const DEFAULT_OPTIONS: Required&amp;lt;SafeJsonParserOptions&amp;gt; = {
  maxBytes: 100 * 1024,
  forbiddenKeys: DEFAULT_FORBIDDEN_KEYS,
}

function normalizeInput(raw: string | Buffer): string {
  if (typeof raw === &quot;string&quot;) {
    return raw
  }

  if (Buffer.isBuffer(raw)) {
    return raw.toString(&quot;utf8&quot;)
  }

  throw new JsonParseError(&quot;Unsupported input type&quot;, {
    code: &quot;INVALID_INPUT_TYPE&quot;,
  })
}

function assertPayloadSize(raw: string, maxBytes: number): void {
  const byteLength = Buffer.byteLength(raw, &quot;utf8&quot;)

  if (byteLength &amp;gt; maxBytes) {
    throw new JsonParseError(
      `JSON payload exceeds the ${maxBytes} byte limit`,
      {
     …</content:encoded></item><item><title>Friday Links #35: Dev Tools, AI &amp; JS Ecosystem Updates</title><link>https://jsdev.space/friday/friday-35/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-35/</guid><description>Discover the latest JavaScript tools, AI dev platforms, frameworks, and ecosystem updates in this week’s curated Friday Links roundup.</description><pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-35.png&quot; alt=&quot;Friday Links #35&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another week, another wave of interesting releases across the JavaScript ecosystem. From emerging developer tools and AI-powered workflows to framework updates and experimental projects, the pace of innovation keeps accelerating.&lt;/p&gt;
&lt;p&gt;In Friday Links #35, we’ve gathered the most notable discoveries worth a developer’s attention — tools that improve productivity, libraries pushing frontend boundaries, and projects that might quietly become tomorrow’s standards.&lt;/p&gt;
&lt;p&gt;Whether you&apos;re building production apps, experimenting with AI tooling, or just staying current with modern web development, this week’s picks have something valuable to explore.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/blog/agentic-future&quot;&gt;Building Next.js for an agentic future&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://adventures.nodeland.dev/archive/yes-learning-to-code-is-still-valuable&quot;&gt;Yes, Learning to Code Is Still Valuable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/potentially-coming-to-a-browser-near-you/&quot;&gt;Potentially Coming to a Browser :near() You &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/death-to-scroll-fade/&quot;&gt;Death to Scroll Fade!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.logrocket.com/react-server-components-performance-mistakes/&quot;&gt;6 React Server Component performance pitfalls in Next.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.hyperparam.app/hightable-scrolling-billions-of-rows/&quot;&gt;Virtual Scrolling for Billions of Rows — Techniques from HighTable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://thecodebarbarian.com/getting-started-with-the-vercel-ai-sdk-in-nodejs.html&quot;&gt;Getting Started with the Vercel AI SDK in Node.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackinsight.dev/blog/loop-performance-empirical-study&quot;&gt;Loop Performance Anti-Patterns: A 40-Repository Scan and Six-Module Benchmark Study&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aarontgrogg.github.io/NoLoJS/&quot;&gt;Reduce the JS Workload with no- or lo-JS options&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stephaniewalter.design/blog/tips-on-how-to-pick-the-right-icons-for-your-website-with-icons8/&quot;&gt;Tips on How to Pick the Right Icons for Your Website&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals&quot;&gt;AGENTS.md outperforms skills in our agent evals&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://una.im/border-shape&quot;&gt;border-shape: the future of the non-rectangular web&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://piccalil.li/blog/an-in-depth-guide-to-customising-lists-with-css/&quot;&gt;An in-depth guide to customising lists with CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/loading-smarter-svg-vs-raster-loaders-in-modern-web-design/&quot;&gt;Loading Smarter: SVG vs. Raster Loaders in Modern Web Design&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.react.doctor/&quot;&gt;React Doctor&lt;/a&gt; - is an open-source CLI tool created by the Million.js (millionco) team that scans React codebases and automatically detects common issues: anti-patterns, performance bottlenecks, accessibility gaps, architectural flaws, and even critical security vulnerabilities that can quietly slip into production.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://svar.dev/react/gantt/&quot;&gt;SVAR React Gantt&lt;/a&gt; - is a modern, high-performance Gantt chart component designed specifically for React applications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://llm-timeline.com/&quot;&gt;LLM Timeline&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://oxc.rs/&quot;&gt;The JavaScript Oxidation Compiler&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://batiste.github.io/blop/example/&quot;&gt;Blop&lt;/a&gt; - A typed language for the web that compiles to Virtual DOM. Blop uses real control flow statements — for loops, if/else — directly in templates, without JSX constraints.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sciter.com/&quot;&gt;Sciter&lt;/a&gt; – Embeddable HTML/CSS/JavaScript Engine for modern UI development&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/taskforcesh/bullmq&quot;&gt;BullMQ&lt;/a&gt; - Message Queue and Batch processing for NodeJS, Python, Elixir and PHP based on Redis&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tanstack/hotkeys&quot;&gt;TanStack Hotkeys&lt;/a&gt; -  Type-Safe keyboard shortcuts library with awesome devtools&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Mina-Massoud/Mina-Rich-Editor&quot;&gt;Mina Rich Editor&lt;/a&gt; - A powerful, elegant rich text editor built with Shadcn UI. Experience unparalleled customization, beautiful design, and seamless integration. Built with React, TypeScript, and meticulous attention to detail.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/Temporal-Playground.png&quot; alt=&quot;Temporal Playground&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://temporal-playground.vercel.app/&quot;&gt;Temporal Playground&lt;/a&gt; — an interactive online environment for experimenting with the JavaScript Temporal API, allowing developers to run real code and explore modern date and time handling directly in the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nicotsx/zerobyte&quot;&gt;Zerobyte&lt;/a&gt; - Powerful backup automation for your remote storage&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/LayoutitStudio/voxcss&quot;&gt;voxcss&lt;/a&gt; -  A CSS voxel engine. A 3D grid for the DOM. Renders HTML cuboids by stacking grid layers and applying transforms.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel-labs/portless&quot;&gt;portless&lt;/a&gt; - Replace port numbers with stable, named .localhost URLs. For humans and agents.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tomkp/react-split-pane&quot;&gt;react-split-pane&lt;/a&gt; -  React split-pane component&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/patrickjuchli/basic-ftp&quot;&gt;Basic FTP&lt;/a&gt; -  FTP client for Node.js, supports FTPS over TLS, passive mode over IPv6, async/await, and Typescript.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sathvikc/lume-js&quot;&gt;Lume.js&lt;/a&gt; - Minimal reactive state management using only standard JavaScript and HTML. No custom syntax, no build step required, no framework lock-in.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/superlucky84/fp-pack&quot;&gt;fp-pack&lt;/a&gt; -  A functional toolkit focused on type-safe pipelines, not FP dogma, for JavaScript and TypeScript.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tambo-ai/tambo&quot;&gt;tambo&lt;/a&gt; -  Generative UI SDK for React&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://biomejs.dev/blog/biome-v2-4/&quot;&gt;Biome v2.4—Embedded Snippets, HTML Accessibility, and Better Framework Support&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/prisma/prisma/releases/tag/7.4.0&quot;&gt;Prsma 7.4.0 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://phaser.io/news/2026/02/phaser-editor-v5-beta&quot;&gt;Phaser Editor v5 Beta now available&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#502---022526&quot;&gt;Emscripten 5.0.2&lt;/a&gt; — the well-established LLVM-to-WebAssembly compiler that enables running native low-level code in Node.js without native bindings — receives internal cleanups, removing legacy Node-specific workarounds that are no longer required.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/honojs/hono/releases/tag/v4.12.0&quot;&gt;Hono 4.12&lt;/a&gt; — a lightweight, multi-runtime web framework built around Web Standards, designed to run seamlessly across Node.js, Bun, Deno, Cloudflare Workers, and other edge environments.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/alfateam/orange-orm/releases/tag/v5.2.0&quot;&gt;Orange ORM 5.2&lt;/a&gt; — a powerful and modern ORM designed for efficient database interaction, offering type-safe queries, clean abstractions, and strong performance across modern JavaScript runtimes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://eslint.org/blog/2026/02/eslint-v10.0.2-released/&quot;&gt;ESLint 10.0.2 Released&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;📺 Videos&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ab01W6h4Giw&quot;&gt;TanStack Router - How to Become a Routing God in React&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0G_HKDrYpYc&quot;&gt;Build Your Own Claude Code with Mastra Workspaces&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uGsauG7Btlg&quot;&gt;Build &amp;amp; Deploy AI Agent Workflow Builder using NextJs, Mongodb, React, Prisma, Upstash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=O7DTIHISrJw&quot;&gt;How One Engineer and AI Crashed IBM&apos;s Stock Price&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ssYt09bCgUY&quot;&gt;The wild rise of OpenClaw...&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=bzWI3Dil9Ig&quot;&gt;My Multi-Agent Team with OpenClaw&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v…</content:encoded></item><item><title>SOLID Principles in React: SRP and OCP</title><link>https://jsdev.space/react-solid-srp-ocp/</link><guid isPermaLink="true">https://jsdev.space/react-solid-srp-ocp/</guid><description>Learn how Single Responsibility and Open Closed principles apply to React components, hooks, and scalable frontend architecture.</description><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;SOLID Principles in React Architecture&lt;/h2&gt;
&lt;p&gt;Software architecture almost never collapses instantly.&lt;/p&gt;
&lt;p&gt;Most React applications begin in a perfectly reasonable state.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A few components.&lt;/li&gt;
&lt;li&gt;Some hooks.&lt;/li&gt;
&lt;li&gt;Clean folders.&lt;/li&gt;
&lt;li&gt;Readable logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything feels simple.&lt;/p&gt;
&lt;p&gt;Then reality arrives.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New product requirements appear.&lt;/li&gt;
&lt;li&gt;APIs evolve.&lt;/li&gt;
&lt;li&gt;Design variants multiply.&lt;/li&gt;
&lt;li&gt;State management spreads across the application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And suddenly something strange happens:&lt;/p&gt;
&lt;p&gt;Changing one feature unexpectedly breaks three unrelated screens.&lt;/p&gt;
&lt;p&gt;At this point developers usually blame React, state management, or framework decisions. But the real problem is almost always architectural.&lt;/p&gt;
&lt;p&gt;The system stopped respecting &lt;strong&gt;responsibilities&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Read more: &lt;a href=&quot;https://jsdev.space/solid-design-principles/&quot;&gt;Understanding SOLID Principles&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why SOLID Still Matters in React&lt;/h2&gt;
&lt;p&gt;SOLID principles were originally introduced for object-oriented programming by Robert C. Martin.&lt;/p&gt;
&lt;p&gt;React is not object-oriented in the classical sense.&lt;/p&gt;
&lt;p&gt;Yet modern React applications map surprisingly well to SOLID ideas:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OOP Concept&lt;/th&gt;
&lt;th&gt;React Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;Hook / handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency&lt;/td&gt;
&lt;td&gt;Service / API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composition&lt;/td&gt;
&lt;td&gt;Component composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abstraction&lt;/td&gt;
&lt;td&gt;Hook interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;React didn’t remove architecture problems.&lt;/p&gt;
&lt;p&gt;It simply changed where they appear.&lt;/p&gt;
&lt;p&gt;Let’s explore the three SOLID principles that have the biggest real-world impact in React systems.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Single Responsibility Principle (SRP) in React&lt;/h2&gt;
&lt;p&gt;Software architecture often sounds abstract until a project starts
growing. Messy components, duplicated logic, and unpredictable bugs
begin to appear.&lt;/p&gt;
&lt;p&gt;The Single Responsibility Principle states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A module should have only one reason to change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In React, responsibilities usually split into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;data logic&lt;/li&gt;
&lt;li&gt;rendering&lt;/li&gt;
&lt;li&gt;composition&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SRP &lt;strong&gt;does not mean&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one function&lt;/li&gt;
&lt;li&gt;small component&lt;/li&gt;
&lt;li&gt;minimal lines of code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, it means:&lt;/p&gt;
&lt;p&gt;A module should change for &lt;strong&gt;one category of reason&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Bad Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function AccountProfile() {
  const [profile, setProfile] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() =&amp;gt; {
    fetch(&quot;/api/profile&quot;)
      .then(res =&amp;gt; res.json())
      .then(data =&amp;gt; {
        setProfile(data);
        setLoading(false);
      });
  }, []);

  if (loading) return &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;;

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;{profile.name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{profile.email}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Component responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fetching data&lt;/li&gt;
&lt;li&gt;managing state&lt;/li&gt;
&lt;li&gt;rendering UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Multiple reasons to change.&lt;/p&gt;
&lt;h3&gt;Correct SRP Architecture&lt;/h3&gt;
&lt;p&gt;A scalable React architecture separates &lt;strong&gt;logic from presentation&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Data Hook&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function useProfileData() {
  const [profile, setProfile] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() =&amp;gt; {
    fetch(&quot;/api/profile&quot;)
      .then(res =&amp;gt; res.json())
      .then(setProfile)
      .finally(() =&amp;gt; setLoading(false));
  }, []);

  return { profile, loading };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This hook answers one question:&lt;/p&gt;
&lt;p&gt;How do we obtain data?&lt;/p&gt;
&lt;p&gt;Nothing else.&lt;/p&gt;
&lt;h3&gt;View Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ProfileView({ profile, loading }) {
  if (loading) return &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;;

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;{profile.name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{profile.email}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pure UI.&lt;/p&gt;
&lt;p&gt;No knowledge about APIs or storage.&lt;/p&gt;
&lt;h3&gt;Container&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ProfileContainer() {
  const state = useProfileData();
  return &amp;lt;ProfileView {...state} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now each part has one responsibility.&lt;/p&gt;
&lt;p&gt;Changing backend logic never affects rendering.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Real SRP Benefit&lt;/h2&gt;
&lt;p&gt;SRP enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;safer refactoring&lt;/li&gt;
&lt;li&gt;reusable UI components&lt;/li&gt;
&lt;li&gt;independent testing&lt;/li&gt;
&lt;li&gt;predictable scaling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Large React applications survive long-term mainly because of this separation.&lt;/p&gt;
&lt;h2&gt;Open Closed Principle (OCP) in React&lt;/h2&gt;
&lt;p&gt;The Open Closed Principle states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Software entities should be open for extension but closed for
modification.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You should add new behavior without rewriting existing components.&lt;/p&gt;
&lt;h3&gt;Typical Violation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ActionButton({ type, onClick }) {
  if (type === &quot;primary&quot;) {
    return &amp;lt;button className=&quot;primary&quot; onClick={onClick}&amp;gt;Primary&amp;lt;/button&amp;gt;;
  }

  if (type === &quot;danger&quot;) {
    return &amp;lt;button className=&quot;danger&quot; onClick={onClick}&amp;gt;Danger&amp;lt;/button&amp;gt;;
  }

  if (type === &quot;success&quot;) {
    return &amp;lt;button className=&quot;success&quot; onClick={onClick}&amp;gt;Success&amp;lt;/button&amp;gt;;
  }

  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every new variation requires modifying the component.&lt;/p&gt;
&lt;p&gt;Over time this becomes fragile and error-prone.&lt;/p&gt;
&lt;h3&gt;OCP-Friendly Approach&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const buttonVariants = {
  primary: &quot;primary&quot;,
  danger: &quot;danger&quot;,
  success: &quot;success&quot;,
};

export function ActionButton({ variant, onClick, children }) {
  return (
    &amp;lt;button
      className={buttonVariants[variant]}
      onClick={onClick}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding new behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;buttonVariants.warning = &quot;warning&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No component modification required.&lt;/p&gt;
&lt;h3&gt;Composition-Based Extension&lt;/h3&gt;
&lt;p&gt;Modern React strongly favors composition.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function Button({ className, ...props }) {
  return &amp;lt;button className={className} {...props} /&amp;gt;;
}

export function DangerButton(props) {
  return &amp;lt;Button className=&quot;danger&quot; {...props} /&amp;gt;;
}

export function PrimaryButton(props) {
  return &amp;lt;Button className=&quot;primary&quot; {...props} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base abstraction remains stable while functionality grows externally.&lt;/p&gt;
&lt;p&gt;This is OCP applied naturally.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Dependency Inversion Principle (DIP)&lt;/h2&gt;
&lt;p&gt;This principle separates scalable architecture from tightly coupled applications.&lt;/p&gt;
&lt;p&gt;Definition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;High-level modules should not depend on low-level modules.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Translated to React:&lt;/p&gt;
&lt;p&gt;UI should not depend directly on infrastructure.&lt;/p&gt;
&lt;h2&gt;Hidden Dependency Problem&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;export function OrdersPage() {
  const [orders, setOrders] = useState([]);

  useEffect(() =&amp;gt; {
    fetch(&quot;/api/orders&quot;)
      .then(r =&amp;gt; r.json())
      .then(setOrders);
  }, []);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component depends directly on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;transport layer&lt;/li&gt;
&lt;li&gt;backend structure&lt;/li&gt;
&lt;li&gt;API implementation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Backend change → UI rewrite.&lt;/p&gt;
&lt;h2&gt;Introducing Abstraction&lt;/h2&gt;
&lt;h3&gt;Provider Interface&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export interface OrdersProvider {
  getOrders(): Promise&amp;lt;any[]&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Concrete Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export class ApiOrdersProvider implements OrdersProvider {
  async getOrders() {
    const res = await fetch(&quot;/api/orders&quot;);
    return res.json();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Hook Depends on Abstraction&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function useOrders(provider: OrdersProvider) {
  const [orders, setOrders] = useState([]);

  useEffect(() =&amp;gt; {
    provider.getOrders().then(setOrders);
  }, [provider]);

  return orders;
}
&lt;/code&gt;&lt;/pr…</content:encoded></item><item><title>SQL Crash Course: JOINs, CTEs, and Window Functions</title><link>https://jsdev.space/sql-complete-guide/</link><guid isPermaLink="true">https://jsdev.space/sql-complete-guide/</guid><description>A complete modern SQL guide covering SELECT, filtering, sorting, aggregates, JOINs, DML/DDL, CTEs, execution order, indexes, and window functions.</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;SQL is one of the very few technologies in software engineering that
does not fade with trends.&lt;/p&gt;
&lt;p&gt;Frameworks change every few years. Backend stacks rotate. Frontend
ecosystems reinvent themselves. But SQL stays.&lt;/p&gt;
&lt;p&gt;That is not nostalgia. That is infrastructure.&lt;/p&gt;
&lt;p&gt;SQL became the universal language of data. Whether a team uses
PostgreSQL, MySQL, ClickHouse, Snowflake, or something distributed and
exotic --- chances are, they still speak SQL.&lt;/p&gt;
&lt;p&gt;Understanding SQL today is not optional for serious developers.&lt;/p&gt;
&lt;p&gt;Backend engineers need it to avoid pushing database logic into
application code. Analysts rely on it daily. QA engineers use it to
validate system state. Product managers use it to inspect metrics
without waiting for analytics pipelines.&lt;/p&gt;
&lt;p&gt;This article walks through SQL step by step --- from simple SELECT
queries to advanced window functions --- using practical examples and a
PostgreSQL‑friendly syntax.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Database Anatomy: What We Actually Work With&lt;/h2&gt;
&lt;p&gt;Before writing queries, it helps to understand what the database really is.&lt;/p&gt;
&lt;p&gt;A relational database is simply structured tables connected by rules. Think of it as Excel with strict discipline — no messy types, no broken references, no silent mistakes.&lt;/p&gt;
&lt;p&gt;Each table contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Columns (structure)&lt;/li&gt;
&lt;li&gt;Rows (actual data)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The power comes from constraints and relationships.&lt;/p&gt;
&lt;p&gt;Unlike spreadsheets, relational databases enforce:&lt;/p&gt;
&lt;p&gt;. Strong typing
. Explicit relationships&lt;/p&gt;
&lt;h3&gt;Primary Keys and Foreign Keys&lt;/h3&gt;
&lt;p&gt;Primary Key (PK) --- unique row identifier.&lt;/p&gt;
&lt;p&gt;Foreign Key (FK) --- reference to another table&apos;s primary key.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;users(id, name)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orders(id, user_id, total)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;orders.user_id&lt;/code&gt; references &lt;code&gt;users.id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This guarantees consistency: the database will reject an order for a
non‑existent user.&lt;/p&gt;
&lt;h3&gt;Common Data Types&lt;/h3&gt;
&lt;p&gt;Most projects rely on a core set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;INT / BIGINT --- numeric identifiers and counters&lt;/li&gt;
&lt;li&gt;TEXT / VARCHAR --- strings&lt;/li&gt;
&lt;li&gt;TIMESTAMPTZ --- date-time stored in UTC&lt;/li&gt;
&lt;li&gt;JSONB (PostgreSQL) --- flexible semi‑structured data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Always store timestamps in UTC. Convert on the client side.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Reading Data: SELECT&lt;/h2&gt;
&lt;p&gt;The most common SQL operation is reading.&lt;/p&gt;
&lt;p&gt;Basic query:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, price
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avoid:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why? Because production systems scale. Fetching unnecessary columns wastes bandwidth, memory, and sometimes performance advantages from indexes.&lt;/p&gt;
&lt;h3&gt;Aliases&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  name AS product_name,
  price AS product_price
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is especially helpful in complex JOIN queries.&lt;/p&gt;
&lt;h3&gt;DISTINCT&lt;/h3&gt;
&lt;p&gt;Use DISTINCT when you care about unique values, not raw rows.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT DISTINCT category
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pagination&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT id, name
FROM products
ORDER BY id
LIMIT 10 OFFSET 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Large OFFSET values are inefficient. Prefer keyset pagination in
high‑load systems:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT id, name
FROM products
WHERE id &amp;gt; 1000
ORDER BY id
LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OFFSET works for small pages. For large datasets, keyset pagination is more efficient.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Filtering and Sorting&lt;/h2&gt;
&lt;p&gt;Filtering uses WHERE.&lt;/p&gt;
&lt;p&gt;Filtering narrows data down to what actually matters.&lt;/p&gt;
&lt;p&gt;Instead of loading everything and filtering in application code, push logic into the database — it is optimized for this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, price
FROM products
WHERE category = &apos;phones&apos;
  AND price &amp;gt; 50000;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;IN&lt;/h3&gt;
&lt;p&gt;Cleaner than multiple OR statements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE category IN (&apos;phones&apos;, &apos;laptops&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;BETWEEN&lt;/h3&gt;
&lt;p&gt;Readable range filter.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE price BETWEEN 30000 AND 60000
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;LIKE / ILIKE&lt;/h3&gt;
&lt;p&gt;Pattern matching for text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE name LIKE &apos;iPhone%&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Postgres case-insensitive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE name ILIKE &apos;iphone%&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;NULL Handling&lt;/h3&gt;
&lt;p&gt;NULL means “unknown”, not empty.&lt;/p&gt;
&lt;p&gt;Incorrect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE description = NULL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WHERE description IS NULL
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ORDER BY&lt;/h3&gt;
&lt;p&gt;Sorting is expensive on large datasets — especially without indexes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, price
FROM products
ORDER BY price DESC, name ASC;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sorting large datasets without indexes is expensive.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Aggregation and GROUP BY&lt;/h2&gt;
&lt;p&gt;Aggregation turns raw data into insight.&lt;/p&gt;
&lt;p&gt;Instead of looking at thousands of rows, we compute metrics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/aggregation-css.png&quot; alt=&quot;Aggregation and GROUP BY&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Aggregate functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COUNT&lt;/li&gt;
&lt;li&gt;SUM&lt;/li&gt;
&lt;li&gt;AVG&lt;/li&gt;
&lt;li&gt;MIN&lt;/li&gt;
&lt;li&gt;MAX&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  MAX(price) AS max_price,
  AVG(price) AS avg_price,
  COUNT(*) AS total_products
FROM products;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GROUP BY&lt;/h3&gt;
&lt;p&gt;Grouping splits rows into logical buckets before aggregation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  category,
  COUNT(*) AS product_count,
  AVG(price) AS avg_price
FROM products
GROUP BY category;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rule:&lt;/p&gt;
&lt;p&gt;Every non-aggregated column in SELECT must appear in GROUP BY.&lt;/p&gt;
&lt;h3&gt;HAVING&lt;/h3&gt;
&lt;p&gt;HAVING filters groups after aggregation — unlike WHERE.&lt;/p&gt;
&lt;p&gt;Incorrect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT category, COUNT(*)
FROM products
WHERE COUNT(*) &amp;gt; 10
GROUP BY category;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT category, COUNT(*)
FROM products
GROUP BY category
HAVING COUNT(*) &amp;gt; 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. JOINs&lt;/h2&gt;
&lt;p&gt;Real SQL begins with JOINs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/sql-joins.png&quot; alt=&quot;JOINs in SQL&quot; /&gt;&lt;/p&gt;
&lt;p&gt;JOINs connect tables. This is where relational databases shine.&lt;/p&gt;
&lt;p&gt;Instead of duplicating data, we combine normalized structures dynamically.&lt;/p&gt;
&lt;h3&gt;INNER JOIN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only matching rows are returned.&lt;/p&gt;
&lt;h3&gt;LEFT JOIN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All users appear. Missing matches become NULL.&lt;/p&gt;
&lt;p&gt;Find users without orders:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SELF JOIN&lt;/h3&gt;
&lt;p&gt;Used when a table references itself (hierarchies, managers, categories).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT e.name AS employee,
       m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Data Modification (DML)&lt;/h2&gt;
&lt;p&gt;These queries change actual data.&lt;/p&gt;
&lt;p&gt;Use carefully.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/data-modification.png&quot; alt=&quot;Data Modification&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;INSERT&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO users (name, email)
VALUES (&apos;Alex&apos;, &apos;alex@example.com&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Always list columns.&lt;/p&gt;
&lt;h3&gt;UPDATE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;UPDATE products
SET price = 65000
WHERE id = 42;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Never run UPDATE without WHERE unless intentional.&lt;/p&gt;
&lt;h3&gt;DELETE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;DELETE FROM users
WHERE last_login &amp;lt; &apos;2020-01-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TRUNCATE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TRUNCATE TABLE logs;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instantly removes all rows.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Schema Changes (DDL)&lt;/h2&gt;
&lt;p&gt;DDL modifies structure, not data.&lt;/p&gt;
&lt;p&gt;Think of it as plumbing — not water.&lt;/p&gt;
&lt;h3&gt;CREATE TABLE&lt;/h3&gt;
&lt;p&gt;Defines structure and constraints.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE orders (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  total NUMERIC(10,2) DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ALTER TABLE&lt;/h3&gt;
&lt;p&gt;Adds or modifies columns safely.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER TABLE orders
ADD COLUMN promo_code TEXT;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DROP TABLE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;DROP TABLE orders;
&lt;/code&gt;&lt;/pre&gt;
&lt;…</content:encoded></item><item><title>Polymorphic Decorators in React: HOCs on Steroids</title><link>https://jsdev.space/polymorphic-hocs-ts/</link><guid isPermaLink="true">https://jsdev.space/polymorphic-hocs-ts/</guid><description>A deep dive into type-safe polymorphic decorators (HOCs) in React with clean logic composition, reusable routing, and advanced TypeScript patterns.</description><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern React applications rarely fail because of rendering complexity.
They fail because logic spreads across components in unpredictable ways.&lt;/p&gt;
&lt;p&gt;Analytics. Routing. Permissions. Feature flags. Loading states. A/B
testing. Logging.&lt;/p&gt;
&lt;p&gt;All of these are cross-cutting concerns. When they are implemented
directly inside JSX trees, components become deeply nested, hard to
maintain, and almost impossible to scale in large teams.&lt;/p&gt;
&lt;p&gt;This article explores a production-grade architectural approach:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Polymorphic decorators implemented as strongly typed Higher-Order
Components (HOCs).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The objectives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Separate logic from rendering&lt;/li&gt;
&lt;li&gt;Preserve strict TypeScript safety&lt;/li&gt;
&lt;li&gt;Enable reusable composition&lt;/li&gt;
&lt;li&gt;Avoid JSX nesting hell&lt;/li&gt;
&lt;li&gt;Scale across large React applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not theory. Everything Here is practical and
production-oriented.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Core Problem: JSX Nesting Explosion&lt;/h2&gt;
&lt;p&gt;Imagine a simple requirement.&lt;/p&gt;
&lt;p&gt;A button must:&lt;/p&gt;
&lt;p&gt;. Generate an href from &lt;code&gt;/product/:id&lt;/code&gt;
. Inject route parameters
. Send analytics on click
. Stay polymorphic
. Remain fully type-safe&lt;/p&gt;
&lt;p&gt;The typical JSX composition quickly becomes unreadable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TrackClick
  as={(props) =&amp;gt; (
    &amp;lt;ResolveRoute
      as={PrimaryButton}
      template=&quot;/product/:id&quot;
      params={{ id: &quot;42&quot; }}
      {...props}
    /&amp;gt;
  )}
  elementKey=&quot;hero-btn&quot;
  eventLabel=&quot;hero_click&quot;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or using asChild-style composition:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ResolveRoute template=&quot;/product/:id&quot; params={{ id: &quot;42&quot; }} asChild&amp;gt;
  &amp;lt;TrackClick elementKey=&quot;hero-btn&quot; eventLabel=&quot;hero_click&quot; asChild&amp;gt;
    &amp;lt;PrimaryButton variant=&quot;solid&quot;&amp;gt;
      Buy Now
    &amp;lt;/PrimaryButton&amp;gt;
  &amp;lt;/TrackClick&amp;gt;
&amp;lt;/ResolveRoute&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though it works, readability suffers.&lt;/p&gt;
&lt;p&gt;Now compare with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const SmartButton = withRouteResolver(
  withAnalyticsTracking(PrimaryButton)
)

&amp;lt;SmartButton
  template=&quot;/product/:id&quot;
  params={{ id: &quot;42&quot; }}
  elementKey=&quot;hero-btn&quot;
  eventLabel=&quot;hero_click&quot;
  variant=&quot;solid&quot;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flat. Predictable. Maintainable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Turning Polymorphic Components into Decorators&lt;/h2&gt;
&lt;p&gt;Classic polymorphic component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {
  ElementType,
  createElement,
  MouseEventHandler,
} from &quot;react&quot;

type MergeProps&amp;lt;Base, Extra&amp;gt; =
  Omit&amp;lt;Base, keyof Extra&amp;gt; &amp;amp; Extra

type PropsOf&amp;lt;T extends ElementType&amp;gt; =
  React.ComponentPropsWithoutRef&amp;lt;T&amp;gt;

function WithSideEffect&amp;lt;
  TComponent extends ElementType&amp;lt;{ onClick?: MouseEventHandler }&amp;gt;
&amp;gt;({
  as,
  onClick,
  sideEffect,
  ...rest
}: MergeProps&amp;lt;
  PropsOf&amp;lt;TComponent&amp;gt;,
  {
    as: TComponent
    sideEffect?: MouseEventHandler
    onClick?: MouseEventHandler
  }
&amp;gt;) {
  const handleClick: MouseEventHandler = (event) =&amp;gt; {
    onClick?.(event)
    sideEffect?.(event)
  }

  return createElement(as, {
    ...rest,
    onClick: handleClick,
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The transformation is simple:&lt;/p&gt;
&lt;p&gt;Remove &lt;code&gt;as&lt;/code&gt; from props and accept the component as a function parameter.&lt;/p&gt;
&lt;p&gt;That converts polymorphic behavior into a reusable decorator.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Analytics Decorator (Production-Ready)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { ComponentType, MouseEventHandler } from &quot;react&quot;

type AnalyticsProps = {
  elementKey: string
  eventLabel?: string
}

export function withAnalyticsTracking&amp;lt;
  TBase extends {
    onClick?: MouseEventHandler
    label?: string
  }
&amp;gt;(BaseComponent: ComponentType&amp;lt;TBase&amp;gt;) {
  return function AnalyticsWrapped(
    props: TBase &amp;amp; AnalyticsProps
  ) {
    const {
      elementKey,
      eventLabel,
      onClick,
      ...rest
    } = props

    const handleClick: MouseEventHandler = (event) =&amp;gt; {
      console.log(&quot;Tracking event:&quot;, {
        elementKey,
        label: eventLabel ?? props.label ?? elementKey,
      })

      onClick?.(event)
    }

    return (
      &amp;lt;BaseComponent
        {...(rest as TBase)}
        onClick={handleClick}
      /&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This decorator:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preserves original props&lt;/li&gt;
&lt;li&gt;Injects analytics&lt;/li&gt;
&lt;li&gt;Keeps type inference intact&lt;/li&gt;
&lt;li&gt;Works with any compatible component&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Route Resolver Decorator&lt;/h2&gt;
&lt;p&gt;Centralized routing logic prevents chaos.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type RouteProps&amp;lt;T extends string&amp;gt; = {
  template: T
  params: Record&amp;lt;string, string&amp;gt;
}

export function withRouteResolver&amp;lt;
  TBase extends { href?: string }
&amp;gt;(BaseComponent: ComponentType&amp;lt;TBase&amp;gt;) {
  return function RouteWrapped&amp;lt;
    TTemplate extends string
  &amp;gt;(props: TBase &amp;amp; RouteProps&amp;lt;TTemplate&amp;gt;) {
    const { template, params, ...rest } = props

    const resolvedHref = Object.entries(params).reduce(
      (url, [key, value]) =&amp;gt;
        url.replace(`:${key}`, value),
      template
    )

    return (
      &amp;lt;BaseComponent
        {...(rest as TBase)}
        href={resolvedHref}
      /&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Template validation&lt;/li&gt;
&lt;li&gt;Central formatting&lt;/li&gt;
&lt;li&gt;Localization integration&lt;/li&gt;
&lt;li&gt;Analytics chaining&lt;/li&gt;
&lt;li&gt;Strict parameter control&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Composing Decorators Safely&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const EnhancedButton = withRouteResolver(
  withAnalyticsTracking(PrimaryButton)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Composition remains predictable because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each decorator isolates logic&lt;/li&gt;
&lt;li&gt;Rendering remains untouched&lt;/li&gt;
&lt;li&gt;TypeScript enforces compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Building a Decoratable Card System&lt;/h2&gt;
&lt;p&gt;Base card:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type CardBaseProps = {
  className?: string
  style?: React.CSSProperties
  children?: React.ReactNode
}

export function CardBase({
  className,
  style,
  children,
}: CardBaseProps) {
  return (
    &amp;lt;div
      className={`rounded-lg p-4 shadow ${className ?? &quot;&quot;}`}
      style={style}
    &amp;gt;
      {children}
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Loading decorator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function withLoadingState&amp;lt;
  T extends { children?: React.ReactNode }
&amp;gt;(BaseComponent: ComponentType&amp;lt;T&amp;gt;) {
  return function LoadingWrapped(
    props: T &amp;amp; { isLoading?: boolean }
  ) {
    const { isLoading, children, ...rest } = props

    return (
      &amp;lt;BaseComponent {...(rest as T)}&amp;gt;
        {isLoading ? &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt; : children}
      &amp;lt;/BaseComponent&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compose utility:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function compose(
  ...decorators: Array&amp;lt;
    (component: ComponentType&amp;lt;any&amp;gt;) =&amp;gt; ComponentType&amp;lt;any&amp;gt;
  &amp;gt;
) {
  return (base: ComponentType&amp;lt;any&amp;gt;) =&amp;gt;
    decorators.reduceRight(
      (acc, decorator) =&amp;gt; decorator(acc),
      base
    )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Final card:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const Card = compose(
  withLoadingState
)(CardBase)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;TypeScript Limitation: Generic Collisions&lt;/h2&gt;
&lt;p&gt;Decorators work perfectly --- until multiple introduce computed
generics.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return function WithRoute&amp;lt;TTemplate extends string&amp;gt;(
  props: ExtractProps&amp;lt;TBase&amp;gt; &amp;amp; LinkProps&amp;lt;TTemplate&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stacking several generic-heavy decorators may:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collapse inference&lt;/li&gt;
&lt;li&gt;Override template literal types&lt;/li&gt;
&lt;li&gt;Reduce safety guarantees&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Practical rule:&lt;/p&gt;
&lt;p&gt;Use only one computed-generic decorator per chain.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where This Pattern Excels&lt;/h2&gt;
&lt;p&gt;Ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Permission layers&lt;/li&gt;
&lt;li&gt;Feature flags&lt;/li&gt;
&lt;li&gt;Centralized routing&lt;/li&gt;
&lt;li&gt;Business rule injection&lt;/li&gt;
&lt;li&gt;UI library extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Less ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Highly dynamic render patterns (FACC is stronger there)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Architectural Impact&lt;/h2&gt;
&lt;p&gt;In large React + TypeScript applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Components are not always under your cont…</content:encoded></item><item><title>Nexus State: A Modern Atomic State Manager for JavaScript</title><link>https://jsdev.space/nexus-state-manager/</link><guid isPermaLink="true">https://jsdev.space/nexus-state-manager/</guid><description>Deep technical overview of Nexus State, a minimal atomic state manager with async atoms, middleware, persistence, and built-in DevTools.</description><pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;State management remains one of the most complex areas in frontend engineering. Over the years, we’ve seen Redux, MobX, Zustand, and Jotai attempt to balance structure, performance, and developer experience.&lt;/p&gt;
&lt;p&gt;Nexus State introduces an atomic model built around simplicity while still offering powerful capabilities such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in DevTools&lt;/li&gt;
&lt;li&gt;Time Travel debugging&lt;/li&gt;
&lt;li&gt;Async state primitives&lt;/li&gt;
&lt;li&gt;Persistence plugins&lt;/li&gt;
&lt;li&gt;Middleware system&lt;/li&gt;
&lt;li&gt;Multi-framework support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article explores its architecture, core patterns, and practical usage.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Core Concept: Atoms&lt;/h2&gt;
&lt;p&gt;Atoms are minimal, isolated pieces of state.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { atom, createStore } from &apos;@nexus-state/core&apos;

const sessionCounter = atom(0, &apos;sessionCounter&apos;)

const multipliedValue = atom((read) =&amp;gt; read(sessionCounter) * 5, &apos;multipliedValue&apos;)

const mainStore = createStore()

mainStore.set(sessionCounter, 4)
console.log(mainStore.get(multipliedValue)) // 20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Derived atoms recompute automatically when dependencies change.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Store &amp;amp; Subscriptions&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const unsubscribeHandler = mainStore.subscribe(sessionCounter, (value) =&amp;gt; {
  console.log(&apos;Updated:&apos;, value)
})

mainStore.set(sessionCounter, 12)
unsubscribeHandler()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The store handles dependency tracking and batched updates.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;React Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { useAtom } from &apos;@nexus-state/react&apos;

const progressAtom = atom(0, &apos;progress&apos;)
const percentAtom = atom((read) =&amp;gt; read(progressAtom) * 10, &apos;percent&apos;)

export function ProgressPanel() {
  const [progress, updateProgress] = useAtom(progressAtom)
  const [percent] = useAtom(percentAtom)

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;Progress: {progress}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Percentage: {percent}%&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; updateProgress(prev =&amp;gt; prev + 1)}&amp;gt;
        Increase
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only components depending on changed atoms re-render.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Async Atoms&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { asyncAtom } from &apos;@nexus-state/async&apos;

const [accountAtom, fetchAccount] = asyncAtom({
  fetchFn: async () =&amp;gt; {
    const response = await fetch(&apos;/api/account&apos;)
    return response.json()
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Async atoms encapsulate loading, error, and resolved states.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Persistence Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { persist, localStorageStorage } from &apos;@nexus-state/persist&apos;

persist(progressAtom, {
  key: &apos;app_progress&apos;,
  storage: localStorageStorage
})(mainStore)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;State is automatically restored on reload.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Middleware Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { middleware } from &apos;@nexus-state/middleware&apos;

const auditMiddleware = middleware(progressAtom, {
  beforeSet: (_, next) =&amp;gt; {
    console.log(&apos;Before:&apos;, next)
    return next
  },
  afterSet: (_, next) =&amp;gt; {
    console.log(&apos;After:&apos;, next)
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Performance Characteristics&lt;/h2&gt;
&lt;p&gt;Nexus State optimizes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Selective component updates&lt;/li&gt;
&lt;li&gt;Batched state mutations&lt;/li&gt;
&lt;li&gt;Lazy evaluation of derived atoms&lt;/li&gt;
&lt;li&gt;Zero DevTools overhead in production builds&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Comparison Snapshot&lt;/h2&gt;
&lt;p&gt;Compared to Redux:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Less boilerplate&lt;/li&gt;
&lt;li&gt;No action types&lt;/li&gt;
&lt;li&gt;Native async support&lt;/li&gt;
&lt;li&gt;Smaller bundle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to Zustand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in Time Travel&lt;/li&gt;
&lt;li&gt;Multi-framework support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to MobX:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicit dependency graph&lt;/li&gt;
&lt;li&gt;Smaller runtime size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to Jotai:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store instance isolation&lt;/li&gt;
&lt;li&gt;Built-in persistence&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;When Nexus State Makes Sense&lt;/h2&gt;
&lt;p&gt;Use it when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want atomic isolation&lt;/li&gt;
&lt;li&gt;You need computed state out of the box&lt;/li&gt;
&lt;li&gt;Debugging tools matter&lt;/li&gt;
&lt;li&gt;You work across multiple frameworks&lt;/li&gt;
&lt;li&gt;You build micro-frontends&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;p&gt;Documentation: &lt;a href=&quot;https://nexus-state.website.yandexcloud.net&quot;&gt;nexus-state.website.yandexcloud.net&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Source Code: &lt;a href=&quot;https://sourcecraft.dev/astashkin-a/nexus-state&quot;&gt;sourcecraft.dev/astashkin-a/nexus-state&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Demo Applications: &lt;a href=&quot;https://codesandbox.io/p/sandbox/nryqsj&quot;&gt;codesandbox.io/p/sandbox/nryqsj&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/algorithm-complexity-big-o/&quot;&gt;Understanding Big O Notation with JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/animejs-animation-guide/&quot;&gt;Mastering Web Animations with Anime.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/await-fetch-slow/&quot;&gt;Optimizing await fetch(): Why It Slows Down and How to Speed It Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to Fix Circular Imports in a React/TypeScript Application</title><link>https://jsdev.space/howto/circular-imports-ts/</link><guid isPermaLink="true">https://jsdev.space/howto/circular-imports-ts/</guid><description>Learn how to detect, understand, and systematically fix circular imports in a React + TypeScript project. Practical examples, module structure diagrams...</description><pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Circular (recursive) imports are one of those issues that look small at first — until they start breaking runtime behavior, tests, hot reload, or production builds.&lt;/p&gt;
&lt;p&gt;They rarely appear as a single obvious mistake. More often, they are a symptom of deeper architectural problems: unclear module boundaries, excessive barrel files, and uncontrolled cross-feature dependencies.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll walk through a practical approach to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Detect circular imports&lt;/li&gt;
&lt;li&gt;Understand why they happen&lt;/li&gt;
&lt;li&gt;Refactor the structure safely&lt;/li&gt;
&lt;li&gt;Prevent them from coming back&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;A Real Module Structure Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;modules/
  client/
    index.ts              # Public API of the module
    redux/
      actions.ts
      reducer.ts
      types.ts
      index.ts
    ui/
      ClientComponent.tsx
    service.ts
    types.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a common structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux/&lt;/code&gt; contains state logic&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ui/&lt;/code&gt; contains components&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service.ts&lt;/code&gt; contains business logic&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.ts&lt;/code&gt; exposes the public API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing looks wrong at first glance. But this structure can easily create circular dependencies if not handled carefully.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Barrel Files: The Hidden Source of Cycles&lt;/h2&gt;
&lt;p&gt;Barrel files (&lt;code&gt;index.ts&lt;/code&gt;) are useful — but dangerous when overused.&lt;/p&gt;
&lt;h3&gt;The Common Mistake&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;modules/index.ts
export * from &quot;./client&quot;;
export * from &quot;./order&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This expands your dependency graph and increases the risk of circular imports.&lt;/p&gt;
&lt;p&gt;Instead of importing specific modules, developers start importing from &lt;code&gt;@modules&lt;/code&gt;, which forces evaluation of everything.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Correct vs Incorrect Imports&lt;/h2&gt;
&lt;h3&gt;Correct&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { clientActions } from &quot;@modules/client&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Incorrect&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { clientActions } from &quot;@modules&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second option hides the dependency path and makes cycles easier to create accidentally.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Internal Module Imports: Always Use Relative Paths&lt;/h2&gt;
&lt;h3&gt;Incorrect&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { clientActions } from &quot;.&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Correct&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { clientActions } from &quot;./redux/actions&quot;;
import { clientTypes } from &quot;./types&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Always use direct relative imports inside the same module.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;High Cohesion: Export Only What Is Public&lt;/h2&gt;
&lt;h3&gt;Incorrect&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export { clientActions, clientReducer } from &quot;./redux&quot;;
export { ClientComponent } from &quot;./ui&quot;;
export { clientTypes } from &quot;./types&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This exposes internal implementation details.&lt;/p&gt;
&lt;h3&gt;Correct&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export { ClientService } from &quot;./service&quot;;
export type { Client } from &quot;./types&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only export what external modules truly need.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Understanding Dependency Graphs&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;utils/                   ← leaf node
 ├─ index.ts
 ├─ format.ts
 └─ validate.ts

modules/
 ├─ client/              ← depends on utils
 │   └─ index.ts
 └─ order/               ← depends on client and utils
     └─ index.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Safe dependency direction:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;utils → client → order
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No back-references. No cycles.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Example of a Circular Dependency&lt;/h2&gt;
&lt;h3&gt;Before&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;client/service.ts
import { orderService } from &quot;@modules/order&quot;;

order/service.ts
import { clientService } from &quot;@modules/client&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dependency graph:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;client → order → client
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;How to Fix It&lt;/h2&gt;
&lt;h3&gt;Option 1: Extract Shared Logic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@modules/shared/types.ts
export type { Client, Order };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now both modules depend on shared:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;client → shared
order  → shared
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;Option 2: Split Large Modules&lt;/h3&gt;
&lt;p&gt;Break a feature into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client-core (business logic)&lt;/li&gt;
&lt;li&gt;client-ui (UI components)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { clientCoreService } from &quot;@modules/client-core&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Smaller modules reduce coupling.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Detecting Circular Imports&lt;/h2&gt;
&lt;p&gt;Recommended tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;madge&lt;/li&gt;
&lt;li&gt;dependency-cruiser&lt;/li&gt;
&lt;li&gt;eslint-plugin-import&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Circular imports are not just annoying errors — they are architectural feedback.&lt;/p&gt;
&lt;p&gt;A clean dependency graph:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Improves maintainability&lt;/li&gt;
&lt;li&gt;Makes testing easier&lt;/li&gt;
&lt;li&gt;Reduces unpredictable runtime behavior&lt;/li&gt;
&lt;li&gt;Keeps teams aligned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The real goal isn’t just “no circular imports”.&lt;/p&gt;
&lt;p&gt;It’s a clean, predictable module architecture.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/howto/env-ts-zod/&quot;&gt;Validate ENV Variables in TypeScript with Zod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/flatmap-doesnt-exist/&quot;&gt;Resolving Missing flat, flatMap, and Flatten on any[] in TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/global-vars-ts/&quot;&gt;Declaring and Managing Global Variables in TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Friday Links #34: Modern JavaScript Picks &amp; Highlights</title><link>https://jsdev.space/friday/friday-34/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-34/</guid><description>A curated roundup of modern JavaScript tools, libraries, releases, and dev resources worth exploring this week — practical picks and notable updates.</description><pubDate>Fri, 13 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./images/friday-34.png&quot; alt=&quot;Friday Links #34&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Friday Links #33 brings together a fresh set of modern JavaScript highlights — from new tools and framework updates to useful libraries and developer resources. This edition focuses on practical discoveries you can try immediately, plus a few notable releases that may shape upcoming workflows. If you like staying current without scrolling through endless feeds, this digest is for you.&lt;/p&gt;
&lt;h2&gt;Pinterest processes more searches than ChatGPT&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./images/pinterest.png&quot; alt=&quot;Pinterest&quot; /&gt;&lt;/p&gt;
&lt;p&gt;According to Pinterest CEO Bill Ready, who made the comparison while discussing the company’s latest quarterly results. He positioned Pinterest as a major standalone search and discovery entry point, especially for commercial intent.&lt;/p&gt;
&lt;p&gt;According to third-party estimates, ChatGPT handles around 75B searches per month, while Pinterest sees roughly 80B, generating about 1.7B monthly clicks. Ready noted that more than half of Pinterest searches are commercial in nature, versus roughly 2% for ChatGPT (by his estimate).&lt;/p&gt;
&lt;p&gt;The quarter itself came in slightly below expectations:
— revenue: $1.32B vs $1.33B expected
— EPS: $0.67 vs $0.69 expected
— Q1 2026 outlook: $951–971M vs $980M expected&lt;/p&gt;
&lt;p&gt;Pinterest attributed the softness to reduced advertiser budgets (especially in Europe) and new tariffs affecting home and furniture categories. Despite this, user growth beat forecasts, reaching 619M monthly active users (+12% YoY). Shares dropped about 20% in after-hours trading.&lt;/p&gt;
&lt;p&gt;The company says it’s doubling down on visual search, recommendations, personalization, and tighter e-commerce integrations (including Amazon partnerships) to capture buying intent earlier in the discovery journey.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-deep-think/&quot;&gt;Gemini 3 Deep Think: Advancing science, research and engineering&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://forwardemail.net/en/blog/docs/email-protocols-rfc-compliance-imap-smtp-pop3-comparison&quot;&gt;Email RFC Protocol Support - Complete Standards &amp;amp; Specifications Guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.logrocket.com/css-in-2026&quot;&gt;CSS in 2026: The new features reshaping frontend development&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://emanueleferonato.com/2026/01/28/greedy-rectangle-merging-turning-binary-grids-into-simple-geometry-javascript-example/&quot;&gt;Greedy Rectangle Merging: Turning Binary Grids into Simple Geometry – JavaScript example&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fadamakis.com/you-probably-dont-need-usecallback-here-7e22d54fe7c0&quot;&gt;You probably don’t need &lt;code&gt;useCallback&lt;/code&gt; here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel-labs/agent-browser&quot;&gt;agent-browser&lt;/a&gt; -  Browser automation CLI for AI agents&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/puffinsoft/syntux&quot;&gt;syntux&lt;/a&gt; -  Generative UIs for the web.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/playfulprogramming/javascript-frameworks-heading-into-2026-2hel&quot;&gt;JavaScript Frameworks - Heading into 2026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/blog/the-browser-hates-surprises/&quot;&gt;The Browser Hates Surprises&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.matuzo.at/blog/2026/text-scaling-meta-tag&quot;&gt;A new meta tag for respecting text scaling on mobile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.phpied.com/measuring-svg-rendering-time/&quot;&gt;Measuring SVG rendering time&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sanity.io/blog/the-logo-soup-problem&quot;&gt;The logo soup problem (and how to solve it)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.developerway.com/posts/debugging-with-ai&quot;&gt;Debugging with AI: Can It Replace an Experienced Developer?&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://npmx.dev/&quot;&gt;npmx&lt;/a&gt; — A Faster, More Informative npm Registry Browser — A new high-performance interface for exploring packages from the official npm registry. Search is quick and accurate, and package pages (for example, axios) surface richer metadata and insights at a glance. It’s not meant to replace the official registry, but it makes the default npmjs.com browsing experience feel dated. The built-in package comparison feature is especially useful.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rari.build/&quot;&gt;Rari&lt;/a&gt; – Rust-powered React framework&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/macaly/almostnode&quot;&gt;almostnode&lt;/a&gt; — Run a Node.js Environment in the Browser — An experimental project that brings a Node.js (v20) runtime directly into the browser, including basic npm package support. It’s still early and not production-ready, but the concept is intriguing and the live demo on the homepage shows promising potential.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/affaan-m/everything-claude-code&quot;&gt;Everything Claude Code&lt;/a&gt; - The complete collection of Claude Code configs from an Anthropic hackathon winner.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/karam-ajaj/atlas&quot;&gt;Atlas - Network Infrastructure Visualizer&lt;/a&gt; -  Open-source tool for network discovery, visualization, and monitoring. Built with Go, FastAPI, and React, supports Docker host scanning.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jeffijoe/awilix&quot;&gt;Awilix&lt;/a&gt; -  Extremely powerful Inversion of Control (IoC) container for Node.JS&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/moscajs/aedes&quot;&gt;Aedes&lt;/a&gt; -  Barebone MQTT broker that can run on any stream server, the node way&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antfu/broz&quot;&gt;broz&lt;/a&gt; -  A simple, frameless browser for screenshots&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/shaka-project/shaka-player&quot;&gt;Shaka Player&lt;/a&gt; -  JavaScript player library / DASH &amp;amp; HLS client / MSE-EME player&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.svg.studio/&quot;&gt;SVG Studio&lt;/a&gt; — A Browser-Based SVG Editing Tool&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://baseline-status-for-video.css-weekly.com/&quot;&gt;Baseline Status for Video&lt;/a&gt; - An Easy Way to Show Baseline Support in Videos. A small, practical utility for quickly displaying Baseline support status in video content. It helps creators visually communicate browser feature support and compatibility without building custom overlays or graphics.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.promptefy.online/&quot;&gt;Promptefy&lt;/a&gt; - Prompt-Driven Video Generator with Gemini&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ShaneIsrael/fireshare&quot;&gt;Fireshare&lt;/a&gt; -  Self host your media and share with unique links&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/fleetbase/fleetbase&quot;&gt;Fleetbase&lt;/a&gt; -  Modular logistics and supply chain operating system (LSOS)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/adesignl/Peek&quot;&gt;Peek&lt;/a&gt; -  Light Weight &quot;Headroom Style&quot; scroll intent library that hides the site header on scroll down and shows on scroll up&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://openai.github.io/chatkit-js/&quot;&gt;ChatKit&lt;/a&gt; — A Framework for Building AI Chat Experiences — A developer framework for adding polished, AI-powered chat interfaces to applications with minimal setup. It provides ready-made building blocks for conversational features and orchestration, helping teams ship advanced chat functionality quickly without rebuilding common patterns from scratch.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/image-js/image-js&quot;&gt;image-js&lt;/a&gt; -  Image processing and manipulation in JavaScript&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/&quot;&gt;TypeScript 6.0&lt;/a&gt; has entered beta. This release isn’t about flashy new features — it focuses on simplifying and cleaning up &lt;code&gt;tsconfig&lt;/code&gt; settings. Think of it as a transitional version designed to smooth the path toward the future Go-based “native” TypeScript 7 compiler.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Improved type inference for functions without &lt;code&gt;this&lt;/code&gt;&lt;/strong&gt;: TypeScript now treats functions that don’t actually use &lt;code&gt;this&lt;/code&gt; as less context-sensitive, improving inference in more cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support for subpath imports starting with &lt;code&gt;#/&lt;/code&gt;&lt;/strong&gt;: You can now use imports like &lt;code&gt;import x from &quot;#/module&quot;&lt;/code&gt; in supported environments, simplifying internal package aliasing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New &lt;code&gt;--stableTypeOrdering&lt;/code&gt; flag&lt;/strong&gt;:…</content:encoded></item><item><title>Choosing the Right State Strategy in React</title><link>https://jsdev.space/react-state-management/</link><guid isPermaLink="true">https://jsdev.space/react-state-management/</guid><description>Learn how to choose and use state management tools in React, including Context, Redux, Zustand, Jotai, MobX, and Valtio.</description><pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;State management in React is both a superpower and a recurring source of complexity. As applications grow, state stops being “just some useState hooks” and turns into a mix of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;local UI state (inputs, dialogs, tabs),&lt;/li&gt;
&lt;li&gt;shared UI state (layout, theme, auth),&lt;/li&gt;
&lt;li&gt;server state (queries, caches, background refresh),&lt;/li&gt;
&lt;li&gt;domain state (entities, workflows, lifecycles),&lt;/li&gt;
&lt;li&gt;and performance constraints (minimizing wasted renders).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no single perfect tool that solves every case. Instead, each approach makes different trade‑offs around &lt;strong&gt;subscriptions&lt;/strong&gt;, &lt;strong&gt;granularity&lt;/strong&gt;, &lt;strong&gt;debuggability&lt;/strong&gt;, and &lt;strong&gt;team workflow&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This article walks through several state management strategies that React developers actually use in production today. Instead of yet another todo list, we will use more realistic examples: a &lt;strong&gt;market dashboard&lt;/strong&gt;, &lt;strong&gt;trade lifecycle&lt;/strong&gt;, &lt;strong&gt;user preferences&lt;/strong&gt;, and &lt;strong&gt;reactive UIs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We will look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When local state is enough.&lt;/li&gt;
&lt;li&gt;When Context helps, and when it hurts.&lt;/li&gt;
&lt;li&gt;How Redux Toolkit enables event logs and workflows.&lt;/li&gt;
&lt;li&gt;How Zustand provides simple global stores with selectors.&lt;/li&gt;
&lt;li&gt;How Jotai models state as small atoms.&lt;/li&gt;
&lt;li&gt;How MobX and Valtio embrace reactive, mutable models.&lt;/li&gt;
&lt;li&gt;How to decide which tool to use for which problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;The Two Fundamental Problems of React State&lt;/h2&gt;
&lt;p&gt;Every state management discussion eventually runs into two opposite pain points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data changes, but the UI does not update.&lt;/strong&gt;&lt;br /&gt;
This is a &lt;em&gt;data flow&lt;/em&gt; problem: React does not know what changed, or the change happened outside its subscription graph.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UI updates even though the relevant data did not change.&lt;/strong&gt;&lt;br /&gt;
This is a &lt;em&gt;subscriptions&lt;/em&gt; problem: components subscribed to too much state or subscribe in a way that always returns new values.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every library in the React ecosystem is, in one way or another, an attempt to make these two problems manageable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Approach 1: Local State with &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useReducer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The simplest and often the most reliable approach is to keep state &lt;strong&gt;local&lt;/strong&gt; to the component that needs it.&lt;/p&gt;
&lt;p&gt;Local state is a great fit for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;widget‑level interactions (modals, dropdowns, accordions);&lt;/li&gt;
&lt;li&gt;form inputs and validation inside a single screen;&lt;/li&gt;
&lt;li&gt;transient data that does not need to be shared.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consider a small &lt;strong&gt;sparkline chart&lt;/strong&gt; that visualizes the last 50 price ticks of a stock. This is pure UI state; nothing else needs to know about it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useReducer, useEffect } from &quot;react&quot;;

interface Candle {
  t: number;
  p: number;
}

type Action = { type: &quot;tick&quot;; price: number };

function chartReducer(state: Candle[], action: Action): Candle[] {
  switch (action.type) {
    case &quot;tick&quot;:
      return [...state.slice(-49), { t: Date.now(), p: action.price }];
    default:
      return state;
  }
}

export function Sparkline({ feed }: { feed: () =&amp;gt; number }) {
  const [candles, dispatch] = useReducer(chartReducer, []);

  useEffect(() =&amp;gt; {
    const id = setInterval(() =&amp;gt; {
      dispatch({ type: &quot;tick&quot;, price: feed() });
    }, 500);
    return () =&amp;gt; clearInterval(id);
  }, [feed]);

  return (
    &amp;lt;pre style={{ fontSize: 12 }}&amp;gt;
      {JSON.stringify(candles.slice(-5), null, 2)}
    &amp;lt;/pre&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;React does not need a state library to manage this. The logic is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;encapsulated,&lt;/li&gt;
&lt;li&gt;easy to test,&lt;/li&gt;
&lt;li&gt;and easy to throw away if the component is refactored.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;When local state is ideal&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The data does not need to be shared across distant components.&lt;/li&gt;
&lt;li&gt;The component can own the entire lifecycle of that state.&lt;/li&gt;
&lt;li&gt;Performance characteristics are simple and local.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;When local state becomes a problem&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Many siblings need the same state and start lifting it up repeatedly.&lt;/li&gt;
&lt;li&gt;Different screens must coordinate around shared entities or workflows.&lt;/li&gt;
&lt;li&gt;Debugging requires inspecting the evolution of state over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As soon as state needs to be &lt;strong&gt;shared or observed by many components&lt;/strong&gt;, we usually escalate beyond pure &lt;code&gt;useState&lt;/code&gt;/&lt;code&gt;useReducer&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Approach 2: React Context for Shared UI State&lt;/h2&gt;
&lt;p&gt;React Context solves a specific problem: &lt;strong&gt;prop drilling&lt;/strong&gt;. Instead of passing props through multiple levels, a Provider at the top of a subtree can expose state and APIs to any descendant.&lt;/p&gt;
&lt;p&gt;Context is a great fit for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;theme,&lt;/li&gt;
&lt;li&gt;locale,&lt;/li&gt;
&lt;li&gt;the current authenticated user,&lt;/li&gt;
&lt;li&gt;feature flags,&lt;/li&gt;
&lt;li&gt;routing metadata,&lt;/li&gt;
&lt;li&gt;“ambient” UI state that rarely changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, Context has a sharp edge: &lt;strong&gt;when the Provider value reference changes, all consumers re‑render.&lt;/strong&gt; Memoization is critical.&lt;/p&gt;
&lt;h3&gt;Example: Shared Watchlist with Context&lt;/h3&gt;
&lt;p&gt;Imagine a market dashboard where multiple components show and modify a user’s stock watchlist.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {
  createContext,
  useContext,
  useCallback,
  useMemo,
  useState,
  ReactNode,
} from &quot;react&quot;;

interface WatchlistContextShape {
  symbols: string[];
  add: (symbol: string) =&amp;gt; void;
  remove: (symbol: string) =&amp;gt; void;
}

const WatchlistContext = createContext&amp;lt;WatchlistContextShape | null&amp;gt;(null);

export function WatchlistProvider({ children }: { children: ReactNode }) {
  const [symbols, setSymbols] = useState&amp;lt;string[]&amp;gt;([&quot;AAPL&quot;, &quot;MSFT&quot;]);

  const add = useCallback(
    (sym: string) =&amp;gt;
      setSymbols(prev =&amp;gt; (prev.includes(sym) ? prev : [...prev, sym])),
    [],
  );

  const remove = useCallback(
    (sym: string) =&amp;gt; setSymbols(prev =&amp;gt; prev.filter(s =&amp;gt; s !== sym)),
    [],
  );

  const value = useMemo(
    () =&amp;gt; ({ symbols, add, remove }),
    [symbols, add, remove],
  );

  return (
    &amp;lt;WatchlistContext.Provider value={value}&amp;gt;
      {children}
    &amp;lt;/WatchlistContext.Provider&amp;gt;
  );
}

export function useWatchlist() {
  const ctx = useContext(WatchlistContext);
  if (!ctx) throw new Error(&quot;WatchlistProvider is missing&quot;);
  return ctx;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any descendant can call &lt;code&gt;useWatchlist()&lt;/code&gt; to read or update the watchlist. For state that changes rarely, this works very well.&lt;/p&gt;
&lt;h3&gt;Pros of Context&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;No external dependencies.&lt;/li&gt;
&lt;li&gt;Perfect for ambient state (theme, locale, auth, feature flags).&lt;/li&gt;
&lt;li&gt;Natural lifetime scoping: unmounting a Provider resets state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons of Context&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;All consumers re‑render whenever &lt;code&gt;value&lt;/code&gt; changes (unless you split Providers).&lt;/li&gt;
&lt;li&gt;No built‑in selectors or fine‑grained subscriptions.&lt;/li&gt;
&lt;li&gt;No action log, no time travel, and limited debugging visibility.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Context is powerful but should not be the default for &lt;strong&gt;high‑frequency&lt;/strong&gt; or &lt;strong&gt;large shared&lt;/strong&gt; state. For those cases, other tools are better suited.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Approach 3: Redux Toolkit for Workflow‑Oriented State&lt;/h2&gt;
&lt;p&gt;Redux was designed around a very specific philosophy:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;State follows actions. Every change is an event in a log.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In its modern form (Redux Toolkit + React‑Redux), Redux is best used when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there are clear business events with semantic meaning,&lt;/li&gt;
&lt;li&gt;debugging requires inspecting &lt;em&gt;how&lt;/em&gt; and &lt;em&gt;when&lt;/em&gt; state changed,&lt;/li&gt;
&lt;li&gt;multiple teams need a shared, predictable architecture,&lt;/li&gt;
&lt;li&gt;reproducibility and time‑travel debugging are important.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Think of a &lt;strong&gt;trade order lifecycle&lt;/strong&gt; in a trading system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;draft → submit…</content:encoded></item><item><title>How to Add Months to a Date in JavaScript</title><link>https://jsdev.space/howto/add-months-to-date-js/</link><guid isPermaLink="true">https://jsdev.space/howto/add-months-to-date-js/</guid><description>Learn reliable ways to add months to a JavaScript Date, accounting for month lengths, edge cases, and timezone differences using native APIs and date-fns.</description><pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Working with dates in JavaScript looks simple until you need real calendar arithmetic. Adding months is a subtle operation because month lengths vary, timezones affect results, and end-of-month behavior is not always obvious.&lt;/p&gt;
&lt;p&gt;Before diving in, if you also work with timezones, you may benefit from this related article:&lt;/p&gt;
&lt;p&gt;Previous related guide (timezone handling):&lt;br /&gt;
&lt;a href=&quot;https://jsdev.space/howto/timezones-date-fns/&quot;&gt;Timezone-Safe Development with date-fns and date-fns-tz
&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Why Adding Months Is Not Just Adding 30 Days&lt;/h2&gt;
&lt;p&gt;Some developers assume that one month equals 30 days:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const date = new Date(&quot;2024-01-31&quot;);
const result = new Date(date.getTime() + 30 * 24 * 60 * 60 * 1000);
console.log(result.toISOString());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does not guarantee an accurate month shift. For January 31, this often produces a date in early March instead of the end of February.&lt;/p&gt;
&lt;h2&gt;Adding Months Using Native JavaScript&lt;/h2&gt;
&lt;p&gt;The native approach uses &lt;code&gt;setMonth&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const date = new Date(&quot;2024-01-31&quot;);
const result = new Date(date);
result.setMonth(result.getMonth() + 1);
console.log(result.toISOString());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this may result in an overflow. January 31 plus one month produces a date in March because February has fewer days.&lt;/p&gt;
&lt;h3&gt;Fixing End-of-Month Behavior&lt;/h3&gt;
&lt;p&gt;If your domain needs end-of-month semantics (billing, subscriptions), you can enforce it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function addMonthsEndSafe(date, months) {
  const d = new Date(date);
  const day = d.getDate();
  d.setMonth(d.getMonth() + months);
  if (d.getDate() &amp;lt; day) {
    d.setDate(0);
  }
  return d;
}

console.log(addMonthsEndSafe(new Date(&quot;2024-01-31&quot;), 1));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This correctly yields &lt;code&gt;2024-02-29&lt;/code&gt; when applicable.&lt;/p&gt;
&lt;h2&gt;Adding Months Using date-fns&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;date-fns&lt;/code&gt; library provides &lt;code&gt;addMonths&lt;/code&gt; for clean edge-case handling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { addMonths } from &quot;date-fns&quot;;

console.log(addMonths(new Date(&quot;2024-01-31&quot;), 1));
// → 2024-02-29
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Subtracting months:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { subMonths } from &quot;date-fns&quot;;

console.log(subMonths(new Date(&quot;2024-05-20&quot;), 3));
// → 2024-02-20
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Handling Timezones&lt;/h2&gt;
&lt;p&gt;If your application involves user timezones, use &lt;code&gt;date-fns-tz&lt;/code&gt; for formatting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { addMonths } from &quot;date-fns&quot;;
import { utcToZonedTime, format } from &quot;date-fns-tz&quot;;

const utc = addMonths(new Date(&quot;2026-01-12T15:00:00.000Z&quot;), 1);
const zone = &quot;America/New_York&quot;;
const local = utcToZonedTime(utc, zone);

console.log(format(local, &quot;yyyy-MM-dd HH:mmXXX&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This helps avoid DST-related formatting issues.&lt;/p&gt;
&lt;h2&gt;Comparison Summary&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;End-of-month safe&lt;/th&gt;
&lt;th&gt;DST aware&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+30 days&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setMonth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom safe function&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Billing scenarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date-fns addMonths&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Most applications&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Choosing the Right Strategy&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Billing&lt;/td&gt;
&lt;td&gt;custom EOM logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI calendars&lt;/td&gt;
&lt;td&gt;date-fns &lt;code&gt;addMonths&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal UTC timestamps&lt;/td&gt;
&lt;td&gt;native &lt;code&gt;setMonth&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finance&lt;/td&gt;
&lt;td&gt;custom logic + UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Adding months is not just arithmetic. Calendar correctness requires accounting for month length differences, handling overflows safely, respecting user timezones when needed, and choosing the right approach for the domain.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;date-fns&lt;/code&gt; simplifies most real-world scenarios, while custom logic may be necessary for financial systems and subscription billing.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/howto/array-flatten/&quot;&gt;Mastering Array Flattening in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/binary-operations-js/&quot;&gt;Mastering Advanced Binary Operations in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/binding-js/&quot;&gt;Mastering Early and Late Binding in TypeScript &amp;amp; JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to Convert a String to a Date in JavaScript</title><link>https://jsdev.space/howto/string-to-date-js/</link><guid isPermaLink="true">https://jsdev.space/howto/string-to-date-js/</guid><description>Learn how to convert date strings into JavaScript Date objects safely, avoid parsing pitfalls, and validate user input.</description><pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Parsing dates from strings in JavaScript can be tricky due to locale differences, ambiguous formats, and browser inconsistencies. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&quot;01/02/2026&quot;&lt;/code&gt; could mean January 2nd or February 1st depending on locale,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;2026-01-12&quot;&lt;/code&gt; parses reliably but may be interpreted in local timezone,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;2026-01-12 10:00&quot;&lt;/code&gt; is parsed differently by different engines,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new Date(string)&lt;/code&gt; handles formats inconsistently.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To avoid these pitfalls, developers should take control over parsing rules rather than relying on automatic behavior.&lt;/p&gt;
&lt;p&gt;Before moving on, if timezones are also part of your workflow, this related article might be useful:&lt;/p&gt;
&lt;p&gt;Related guide (timezone handling):
&lt;a href=&quot;https://jsdev.space/howto/timezones-date-fns/&quot;&gt;Timezone-Safe Development with date-fns and date-fns-tz&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Built‑in Parsing: Pitfalls&lt;/h2&gt;
&lt;p&gt;Avoid relying on native parsing for non-ISO strings:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Date(&quot;2026-01-12 10:00&quot;); // ambiguous, local parsing rules
new Date(&quot;01/12/2026&quot;);      // locale-dependent
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Better:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Date(&quot;2026-01-12T10:00:00Z&quot;); // explicit UTC
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or using a parsing library like &lt;code&gt;date-fns&lt;/code&gt; to enforce expected formats.&lt;/p&gt;
&lt;h2&gt;Using date-fns for Safe Parsing&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;date-fns&lt;/code&gt; enables explicit parsing patterns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { parse, isValid } from &quot;date-fns&quot;;

const input = &quot;12.01.2026&quot;;
const pattern = &quot;dd.MM.yyyy&quot;;
const reference = new Date();

const result = parse(input, pattern, reference);

if (isValid(result)) {
  console.log(result.toISOString());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents locale ambiguity and ensures reproducibility.&lt;/p&gt;
&lt;h2&gt;React Form Parsing Demo (Practical Example)&lt;/h2&gt;
&lt;p&gt;For more advanced form handling patterns, see&lt;br /&gt;
&lt;a href=&quot;https://jsdev.space/react-form-primitives/&quot;&gt;&lt;strong&gt;React Form Primitives:&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The following example demonstrates how to parse a date string entered by a user inside a React controlled form, validate it, and show a formatted output.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { useState } from &quot;react&quot;;
import { parse, isValid, format } from &quot;date-fns&quot;;

type SupportedFormat = &quot;yyyy-MM-dd&quot; | &quot;dd.MM.yyyy&quot; | &quot;MM/dd/yyyy&quot;;

const formatExamples: Record&amp;lt;SupportedFormat, string&amp;gt; = {
  &quot;yyyy-MM-dd&quot;: &quot;2026-01-12&quot;,
  &quot;dd.MM.yyyy&quot;: &quot;12.01.2026&quot;,
  &quot;MM/dd/yyyy&quot;: &quot;01/12/2026&quot;,
};

export function DateStringForm() {
  const [raw, setRaw] = useState(&quot;&quot;);
  const [pattern, setPattern] = useState&amp;lt;SupportedFormat&amp;gt;(&quot;yyyy-MM-dd&quot;);
  const [parsed, setParsed] = useState&amp;lt;Date | null&amp;gt;(null);
  const [error, setError] = useState&amp;lt;string | null&amp;gt;(null);

  const handleSubmit: React.FormEventHandler = (e) =&amp;gt; {
    e.preventDefault();
    const trimmed = raw.trim();
    if (!trimmed) {
      setParsed(null);
      setError(&quot;Please enter a date string.&quot;);
      return;
    }
    const reference = new Date();
    const result = parse(trimmed, pattern, reference);
    if (!isValid(result)) {
      setParsed(null);
      setError(
        \`Cannot parse &quot;\${trimmed}&quot; as \${pattern}. Example: \${formatExamples[pattern]}\`
      );
      return;
    }
    setParsed(result);
    setError(null);
  };

  return (
    &amp;lt;div style={{ maxWidth: 480, margin: &quot;2rem auto&quot;, fontFamily: &quot;system-ui&quot; }}&amp;gt;
      &amp;lt;h2&amp;gt;String to Date parsing demo&amp;lt;/h2&amp;gt;
      &amp;lt;p style={{ marginBottom: &quot;1rem&quot; }}&amp;gt;
        This example shows how to convert a date string into a JavaScript{&quot; &quot;}
        &amp;lt;code&amp;gt;Date&amp;lt;/code&amp;gt; object using controlled form inputs and{&quot; &quot;}
        &amp;lt;code&amp;gt;date-fns&amp;lt;/code&amp;gt;. For more advanced form patterns, see{&quot; &quot;}
        &amp;lt;a
          href=&quot;https://jsdev.space/react-form-primitives/&quot;
          target=&quot;_blank&quot;
          rel=&quot;noreferrer&quot;
        &amp;gt;
          React Form Primitives
        &amp;lt;/a&amp;gt;
        .
      &amp;lt;/p&amp;gt;

      &amp;lt;form onSubmit={handleSubmit} style={{ display: &quot;grid&quot;, gap: &quot;0.75rem&quot; }}&amp;gt;
        &amp;lt;label style={{ display: &quot;grid&quot;, gap: &quot;0.25rem&quot; }}&amp;gt;
          &amp;lt;span&amp;gt;Date string&amp;lt;/span&amp;gt;
          &amp;lt;input
            type=&quot;text&quot;
            value={raw}
            onChange={(e) =&amp;gt; setRaw(e.target.value)}
            placeholder={formatExamples[pattern]}
            style={{
              padding: &quot;0.5rem 0.75rem&quot;,
              borderRadius: 6,
              border: &quot;1px solid #ccc&quot;,
              fontFamily: &quot;inherit&quot;,
            }}
          /&amp;gt;
        &amp;lt;/label&amp;gt;

        &amp;lt;label style={{ display: &quot;grid&quot;, gap: &quot;0.25rem&quot; }}&amp;gt;
          &amp;lt;span&amp;gt;Expected format&amp;lt;/span&amp;gt;
          &amp;lt;select
            value={pattern}
            onChange={(e) =&amp;gt; setPattern(e.target.value as SupportedFormat)}
            style={{
              padding: &quot;0.5rem 0.75rem&quot;,
              borderRadius: 6,
              border: &quot;1px solid #ccc&quot;,
              fontFamily: &quot;inherit&quot;,
            }}
          &amp;gt;
            &amp;lt;option value=&quot;yyyy-MM-dd&quot;&amp;gt;yyyy-MM-dd (2026-01-12)&amp;lt;/option&amp;gt;
            &amp;lt;option value=&quot;dd.MM.yyyy&quot;&amp;gt;dd.MM.yyyy (12.01.2026)&amp;lt;/option&amp;gt;
            &amp;lt;option value=&quot;MM/dd/yyyy&quot;&amp;gt;MM/dd/yyyy (01/12/2026)&amp;lt;/option&amp;gt;
          &amp;lt;/select&amp;gt;
        &amp;lt;/label&amp;gt;

        &amp;lt;button
          type=&quot;submit&quot;
          style={{
            marginTop: &quot;0.5rem&quot;,
            padding: &quot;0.5rem 0.75rem&quot;,
            borderRadius: 6,
            border: &quot;none&quot;,
            background: &quot;#111827&quot;,
            color: &quot;white&quot;,
            cursor: &quot;pointer&quot;,
            fontFamily: &quot;inherit&quot;,
          }}
        &amp;gt;
          Parse date
        &amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;

      &amp;lt;div style={{ marginTop: &quot;1rem&quot;, fontSize: 14 }}&amp;gt;
        {error &amp;amp;&amp;amp; (
          &amp;lt;p style={{ color: &quot;#b91c1c&quot;, margin: 0 }}&amp;gt;
            {error}
          &amp;lt;/p&amp;gt;
        )}

        {parsed &amp;amp;&amp;amp; !error &amp;amp;&amp;amp; (
          &amp;lt;div
            style={{
              marginTop: &quot;0.75rem&quot;,
              padding: &quot;0.75rem&quot;,
              borderRadius: 6,
              background: &quot;#f9fafb&quot;,
              border: &quot;1px solid #e5e7eb&quot;,
            }}
          &amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;strong&amp;gt;Parsed Date object:&amp;lt;/strong&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;code&amp;gt;{parsed.toString()}&amp;lt;/code&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div style={{ marginTop: &quot;0.5rem&quot; }}&amp;gt;
              &amp;lt;strong&amp;gt;ISO (UTC):&amp;lt;/strong&amp;gt;{&quot; &quot;}
              &amp;lt;code&amp;gt;{parsed.toISOString()}&amp;lt;/code&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div style={{ marginTop: &quot;0.25rem&quot; }}&amp;gt;
              &amp;lt;strong&amp;gt;Formatted (yyyy-MM-dd):&amp;lt;/strong&amp;gt;{&quot; &quot;}
              &amp;lt;code&amp;gt;{format(parsed, &quot;yyyy-MM-dd&quot;)}&amp;lt;/code&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Key takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Native &lt;code&gt;Date&lt;/code&gt; parsing is inconsistent for non-ISO formats,&lt;/li&gt;
&lt;li&gt;Using explicit parsing rules prevents locale ambiguity,&lt;/li&gt;
&lt;li&gt;React integration is straightforward with controlled inputs,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;date-fns&lt;/code&gt; improves reliability and validation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This updated MDX now includes both conceptual guidance and a practical UI demo.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/howto/add-months-to-date-js/&quot;&gt;Practical Month Arithmetic and Calendar Logic in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/array-flatten/&quot;&gt;Mastering Array Flattening in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/binary-operations-js/&quot;&gt;Mastering Advanced Binary Operations in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to Handle Timezones with date-fns and date-fns-tz</title><link>https://jsdev.space/howto/timezones-date-fns/</link><guid isPermaLink="true">https://jsdev.space/howto/timezones-date-fns/</guid><description>Learn how to manage timezones, UTC conversions, and scheduling logic using date-fns, date-fns-tz, and PostgreSQL in real-world applications.</description><pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Why Timezones Matter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Working with dates is deceptively complex. Different regions have local timezone offsets, daylight saving rules (DST), and historical changes.&lt;/p&gt;
&lt;p&gt;Almost every backend stores timestamps in &lt;strong&gt;UTC&lt;/strong&gt;, while frontends must show them in &lt;strong&gt;local time&lt;/strong&gt;. Mistakes lead to wrong displayed times, off-by-one-day bugs, incorrect scheduling, and misaligned business rules.&lt;/p&gt;
&lt;p&gt;Instead of building logic manually, this article uses &lt;code&gt;date-fns&lt;/code&gt; and &lt;code&gt;date-fns-tz&lt;/code&gt;, two lightweight libraries designed for timezone-safe manipulation.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;npm install date-fns date-fns-tz
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Basic Concepts&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UTC is the universal storage format&lt;/strong&gt;&lt;br /&gt;
Example: &lt;code&gt;&quot;2024-10-11T10:30:00.000Z&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Local time is a presentation layer&lt;/strong&gt;&lt;br /&gt;
UI must render times in the user’s timezone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;date-fns-tz&lt;/code&gt; gives explicit control&lt;/strong&gt;&lt;br /&gt;
Prevents “magic conversion bugs”.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Parsing a UTC Timestamp into Local Time&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { format } from &quot;date-fns&quot;;
import { utcToZonedTime } from &quot;date-fns-tz&quot;;

const iso = &quot;2026-01-12T15:00:00.000Z&quot;;
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;

const date = utcToZonedTime(iso, timezone);
console.log(format(date, &quot;yyyy-MM-dd HH:mm:ssXXX&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Converting Local Time to UTC&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { zonedTimeToUtc } from &quot;date-fns-tz&quot;;

const userInput = &quot;2026-01-12 18:30:00&quot;;
const timezone = &quot;Europe/Berlin&quot;;

const utc = zonedTimeToUtc(userInput, timezone);
console.log(utc.toISOString());
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Formatting With Timezones&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { format, formatISO } from &quot;date-fns&quot;;
import { utcToZonedTime } from &quot;date-fns-tz&quot;;

const iso = &quot;2026-07-01T12:00:00Z&quot;;
const zone = &quot;America/New_York&quot;;

const local = utcToZonedTime(iso, zone);

console.log(format(local, &quot;PPpp&quot;));
console.log(formatISO(local));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Handling Daylight Saving Time (DST)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const winter = &quot;2026-01-10T12:00:00Z&quot;;
const summer = &quot;2026-07-10T12:00:00Z&quot;;
const zone = &quot;America/New_York&quot;;

console.log(format(utcToZonedTime(winter, zone), &quot;HH:mmXXX&quot;));
console.log(format(utcToZonedTime(summer, zone), &quot;HH:mmXXX&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Comparing Dates Across Timezones&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { getTime } from &quot;date-fns&quot;;

const same = getTime(new Date(a)) === getTime(new Date(b));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Scheduling Use Case&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { zonedTimeToUtc } from &quot;date-fns-tz&quot;;

function schedule(userLocalDateTime, userZone) {
  const utc = zonedTimeToUtc(userLocalDateTime, userZone);
  return utc.toISOString();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For rendering:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { utcToZonedTime } from &quot;date-fns-tz&quot;;

function present(utcIso, userZone) {
  return utcToZonedTime(utcIso, userZone);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Browser Timezones&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Common Pitfalls&lt;/h2&gt;
&lt;p&gt;Wrong:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Date(&quot;2026-01-12 10:00&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Date(&quot;2026-01-12T10:00:00Z&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or convert using &lt;code&gt;zonedTimeToUtc&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;When to Use Which Function&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UTC → local&lt;/td&gt;
&lt;td&gt;&lt;code&gt;utcToZonedTime(utc, zone)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local → UTC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zonedTimeToUtc(local, zone)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format&lt;/td&gt;
&lt;td&gt;&lt;code&gt;format(date, pattern)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date.toISOString()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Working with timezones is not simple subtraction. It requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;consistent UTC storage,&lt;/li&gt;
&lt;li&gt;correct local presentation,&lt;/li&gt;
&lt;li&gt;DST-aware conversions,&lt;/li&gt;
&lt;li&gt;predictable comparisons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;date-fns&lt;/code&gt; and &lt;code&gt;date-fns-tz&lt;/code&gt; help avoid most timezone-related pitfalls while staying lightweight.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/howto/add-months-to-date-js/&quot;&gt;Practical Month Arithmetic and Calendar Logic in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/array-flatten/&quot;&gt;Mastering Array Flattening in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/binary-operations-js/&quot;&gt;Mastering Advanced Binary Operations in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Howto Build Reactive Declarative UI in Vanilla JavaScript</title><link>https://jsdev.space/howto/reactive-vanilla-js/</link><guid isPermaLink="true">https://jsdev.space/howto/reactive-vanilla-js/</guid><description>A practical experiment using vanilla JavaScript, Web APIs, and Proxy-based state to create reactive, declarative UI without frameworks.</description><pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern UI frameworks offer abstraction layers that make user interfaces declarative and reactive. However, the web platform itself exposes primitives that can be composed to achieve similar patterns without introducing a dedicated UI library. This article demonstrates an experimental approach for creating a reactive, declarative UI flow using only &lt;strong&gt;vanilla JavaScript&lt;/strong&gt;, &lt;strong&gt;Web APIs&lt;/strong&gt;, and &lt;strong&gt;Proxy-based state tracking&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The purpose of the experiment is to examine how far native capabilities can be pushed without framework-level abstractions and to illustrate architectural benefits of declarative behavior in UI code: improved clarity, maintainability, and reduced coupling.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Target Behavior&lt;/h2&gt;
&lt;p&gt;The experiment focuses on a practical business scenario:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Display a modal dialog that performs periodic polling of an API endpoint. The dialog should remain open until a specific condition is met, then resolve or reject accordingly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The modal dynamically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mounts itself into the DOM&lt;/li&gt;
&lt;li&gt;starts and manages a polling process&lt;/li&gt;
&lt;li&gt;exposes reactive internal state&lt;/li&gt;
&lt;li&gt;updates based on the polling result&lt;/li&gt;
&lt;li&gt;closes automatically when finished&lt;/li&gt;
&lt;li&gt;provides optional developer controls&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The primary requirement is that &lt;strong&gt;consumer code defines what should happen, not how to wire it&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Declarative Usage Example&lt;/h2&gt;
&lt;p&gt;Example invocation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uiModalEngine.showPollingDialog({
  endpoint: `${getServiceBaseUrl()}/process/wait_for/confirmation`,

  requestPayload: () =&amp;gt; ({
    taskId: currentTask.id,
    mode: &quot;rapid&quot;,
    includeAudit: true,
  }),

  requestOptions: {
    method: &quot;POST&quot;,
    headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
  },

  shouldContinue: (response) =&amp;gt; response.ok &amp;amp;&amp;amp; response.pending === true,

  intervalMs: 1000,

  buildContent: (mountNode) =&amp;gt; {
    const contentBlock = uiModalEngine.createContentBlock({
      title: &quot;Waiting for confirmation...&quot;,
      description: &quot;This dialog will close automatically once the operation completes.&quot;,
    })
    mountNode.appendChild(contentBlock)
  },

  onResolved: ({ dialogNode, response }) =&amp;gt; {
    metrics.track(&quot;operation_confirmed&quot;)
    dialogNode.remove()
  },

  onRejected: ({ dialogNode, error }) =&amp;gt; {
    logger.error(&quot;operation_polling_failed&quot;, error)
    dialogNode.remove()
  },

  devToolsEnabled: false,
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Declarative Takeaways&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Ownership&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI behavior&lt;/td&gt;
&lt;td&gt;Declarative configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI rendering&lt;/td&gt;
&lt;td&gt;Modal orchestrator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOM structure&lt;/td&gt;
&lt;td&gt;DOM utility layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;polling logic&lt;/td&gt;
&lt;td&gt;polling helper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reactive state&lt;/td&gt;
&lt;td&gt;Proxy-based tracker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No framework is involved, yet responsibilities remain clearly segmented.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Core Building Block: DOM Utility Layer&lt;/h2&gt;
&lt;p&gt;To keep high-level code focused on behavior, DOM creation is delegated to a lightweight utility:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class DomToolkit {
  constructor(doc) {
    this.doc = doc
  }

  static getInstance(doc) {
    if (!DomToolkit.instance) DomToolkit.instance = new DomToolkit(doc)
    return DomToolkit.instance
  }

  createElement({ tag, classes, id, attrs = {}, styles = {}, html }) {
    const el = this.doc.createElement(tag)

    if (id) el.id = id

    if (typeof classes === &quot;string&quot;) el.classList.add(classes)
    if (Array.isArray(classes)) classes.forEach(c =&amp;gt; el.classList.add(c))

    Object.entries(attrs).forEach(([k, v]) =&amp;gt; el.setAttribute(k, v))
    Object.entries(styles).forEach(([k, v]) =&amp;gt; el.style[k] = v)

    if (html != null) el.innerHTML = html

    return el
  }
}

const domToolkit = DomToolkit.getInstance(document)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This removes boilerplate from business logic and centralizes standard element configuration.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Reactive State via Proxy&lt;/h2&gt;
&lt;p&gt;Next, the experiment introduces deep reactive state using the native &lt;code&gt;Proxy&lt;/code&gt; object. This allows mutations at arbitrary depth to be observed without requiring explicit setters.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class DeepStateProxy {
  constructor(target, { onSet, onDelete } = {}) {
    this.onSet = onSet
    this.onDelete = onDelete
    return this.wrap(target, [])
  }

  wrap(node, path) {
    if (!node || typeof node !== &quot;object&quot;) return node

    const handler = {
      set: (target, key, value) =&amp;gt; {
        const fullPath = [...path, key]
        target[key] = this.wrap(value, fullPath)
        this.onSet?.(value, fullPath)
        return true
      },
      deleteProperty: (target, key) =&amp;gt; {
        if (!(key in target)) return false
        const fullPath = [...path, key]
        delete target[key]
        this.onDelete?.(fullPath)
        return true
      },
    }

    Object.keys(node).forEach(k =&amp;gt; {
      node[k] = this.wrap(node[k], [...path, k])
    })

    return new Proxy(node, handler)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const state = new DeepStateProxy({
  attempts: 0,
  lastResponse: null,
}, {
  onSet: (value, path) =&amp;gt; console.debug(&quot;state changed:&quot;, path.join(&quot;.&quot;), value),
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach:&lt;/p&gt;
&lt;p&gt;enables deep mutation tracking&lt;br /&gt;
does not require libraries&lt;br /&gt;
keeps state as plain objects&lt;br /&gt;
keeps consumer code minimal&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Polling Logic as a Reusable Abstraction&lt;/h2&gt;
&lt;p&gt;To isolate asynchronous logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function runPolling({ task, shouldStop, intervalMs }) {
  while (true) {
    const result = await task()
    if (shouldStop(result)) return result
    await new Promise(res =&amp;gt; setTimeout(res, intervalMs))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isolating polling enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;testability (polling logic has no DOM dependencies)&lt;/li&gt;
&lt;li&gt;readability (consumer describes behavior declaratively)&lt;/li&gt;
&lt;li&gt;reusability (polling can be embedded into other flows)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Modal Orchestrator&lt;/h2&gt;
&lt;p&gt;The orchestrator integrates DOM utilities, polling, and reactive state into a coherent unit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ModalOrchestrator.prototype.showPollingDialog = function (cfg) {
  const {
    endpoint, requestPayload, requestOptions,
    shouldContinue, intervalMs,
    buildContent, onResolved, onRejected,
    devToolsEnabled = false,
  } = cfg

  const dialogNode = this.createDialogShell({ buildContent })
  document.body.appendChild(dialogNode)

  const state = new DeepStateProxy({
    attempts: 0,
    polling: true,
    aborted: false,
    lastResponse: { ok: false },
  }, {
    onSet: (value, path) =&amp;gt; {
      if (devToolsEnabled) console.debug(&quot;state:&quot;, path.join(&quot;.&quot;), value)
    },
    onDelete: () =&amp;gt; { throw new Error(&quot;state mutation violation&quot;) },
  })

  state.polling = true

  runPolling({
    task: async () =&amp;gt; {
      const payload = requestPayload()
      const res = await fetch(endpoint, { ...requestOptions, body: JSON.stringify(payload) })
        .then(r =&amp;gt; r.json())
        .catch(err =&amp;gt; ({ ok: false, error: err.message, errored: true }))

      if (!shouldContinue(res) &amp;amp;&amp;amp; !res.errored) state.polling = false
      else state.attempts++

      state.lastResponse = res
      return res
    },
    shouldStop: () =&amp;gt; !state.polling,
    intervalMs,
  })
    .then(res =&amp;gt; onResolved?.({ dialogNode, response: res }))
    .catch(err =&amp;gt; onRejected?.({ dialogNode, error: err }))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the absence of framework-specific concepts such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;components&lt;/li&gt;
&lt;li&gt;hooks&lt;/li&gt;
&lt;li&gt;virtual DOM&lt;/li&gt;
&lt;li&gt;stores&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yet the intent remains clear and maintainable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Observations &amp;amp; Takeaways&lt;/h2&gt;
&lt;p&gt;Key architectural observations include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Declarative descriptions scale better than imperative wiring&lt;/strong&gt;&lt;br /&gt;
The consumer code reads…</content:encoded></item><item><title>Meet Ripple: The Elegant TypeScript UI Framework</title><link>https://jsdev.space/meet-ripple/</link><guid isPermaLink="true">https://jsdev.space/meet-ripple/</guid><description>Ripple is a compiler-first TypeScript UI framework for building fast, clean, reactive applications with minimal boilerplate and optimal performance.</description><pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.ripplejs.com/&quot;&gt;Ripple&lt;/a&gt; is a compiler-first TypeScript UI framework for building fast, clean, reactive applications with minimal boilerplate and optimal performance.&lt;/p&gt;
&lt;h2&gt;Why the Frontend World Needs Ripple in 2026&lt;/h2&gt;
&lt;p&gt;Front-end development has reached an unusual point in its history:&lt;br /&gt;
writing code is easy — maintaining it is hard.&lt;/p&gt;
&lt;p&gt;AI accelerated code output, but did not solve code &lt;strong&gt;quality&lt;/strong&gt;, &lt;strong&gt;consistency&lt;/strong&gt;, or &lt;strong&gt;review overhead&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Traditional frameworks are powerful but often come with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;verbose state handling&lt;/li&gt;
&lt;li&gt;over-rendering components&lt;/li&gt;
&lt;li&gt;heavy abstraction layers&lt;/li&gt;
&lt;li&gt;confusing refs/signals/hooks&lt;/li&gt;
&lt;li&gt;bloated bundle sizes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ripple was designed for this moment. It prioritizes simplicity, clarity, and reactivity.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Code should read like it does.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;What is Ripple?&lt;/h2&gt;
&lt;p&gt;Ripple is a &lt;strong&gt;compiler-first, fine-grained reactive UI framework&lt;/strong&gt; with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript-first components&lt;/li&gt;
&lt;li&gt;reactive variables with &lt;code&gt;track()&lt;/code&gt; + &lt;code&gt;@&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;no Virtual DOM&lt;/li&gt;
&lt;li&gt;automatic dependency tracking&lt;/li&gt;
&lt;li&gt;inline control flow&lt;/li&gt;
&lt;li&gt;scoped CSS&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Ripple’s Design Goals&lt;/h2&gt;
&lt;h3&gt;1. Compiler Before Runtime&lt;/h3&gt;
&lt;p&gt;The compiler performs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DOM dependency analysis&lt;/li&gt;
&lt;li&gt;dead CSS removal&lt;/li&gt;
&lt;li&gt;scoped styling&lt;/li&gt;
&lt;li&gt;code transformation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Reactive by Default&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;let count = track(0);
&amp;lt;button onClick={() =&amp;gt; @count++}&amp;gt;{@count}&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;ref()&lt;/code&gt;, &lt;code&gt;.value&lt;/code&gt;, &lt;code&gt;$:&lt;/code&gt;, or signals.&lt;/p&gt;
&lt;h3&gt;3. Low Cognitive Load&lt;/h3&gt;
&lt;p&gt;Less to memorize. Business logic remains obvious.&lt;/p&gt;
&lt;h3&gt;4. Granular DOM Updates&lt;/h3&gt;
&lt;p&gt;Only updated nodes mutate — not whole components.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Initialize a new project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx create-ripple-app ripple-todo-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move inside:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ripple-todo-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If integrating manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install ripple ripple-compiler ripple-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Scripts&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm run dev
npm run build
npm run preview
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Folder Structure&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;my-ripple-app/
├─ src/
│  ├─ App.ripple
│  ├─ index.tsx
│  └─ components/
├─ public/
├─ ripple.config.ts
├─ tsconfig.json
├─ package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Verify Setup&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;component App() {
  &amp;lt;h1&amp;gt;{&quot;Hello Ripple&quot;}&amp;lt;/h1&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it renders, you&apos;re ready.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Ripple in 2 Minutes: Core Syntax&lt;/h2&gt;
&lt;h3&gt;Reactive Variables&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;let count = track(0);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Read + Write&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button onClick={() =&amp;gt; @count++}&amp;gt;{@count}&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Reactive Collections&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const todos = #[];
const user = #{ name: &quot;Tom&quot; };
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Components&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;component Greeting({ name }) {
  &amp;lt;h1&amp;gt;{&quot;Hello &quot;}{name}&amp;lt;/h1&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inline Control Flow&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (const item of items) {
  &amp;lt;Item data={item}/&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Productivity Advantages&lt;/h2&gt;
&lt;p&gt;Ripple reduces maintenance cost by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fewer primitives&lt;/li&gt;
&lt;li&gt;direct reactivity&lt;/li&gt;
&lt;li&gt;compiler constraints&lt;/li&gt;
&lt;li&gt;minimal boilerplate&lt;/li&gt;
&lt;li&gt;tiny runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Building a Real Demo: Todo List&lt;/h2&gt;
&lt;p&gt;We’ll demonstrate Ripple’s power with a fully reactive Todo List.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;TodoInput&lt;/code&gt; Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { track } from &quot;ripple&quot;;

component TodoInput({ onAdd }) {
  let text = track(&quot;&quot;);

  function submit() {
    const v = @text.trim();
    if (v) {
      onAdd(v);
      @text = &quot;&quot;;
    }
  }

  &amp;lt;div class=&quot;input&quot;&amp;gt;
    &amp;lt;input
      placeholder=&quot;Add a task...&quot;
      value={@text}
      onInput={(e) =&amp;gt; @text = e.target.value}
      onKeyDown={(e) =&amp;gt; { if (e.key === &quot;Enter&quot;) submit(); }}
    /&amp;gt;
    &amp;lt;button onClick={submit}&amp;gt;{&quot;Add&quot;}&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;TodoItem&lt;/code&gt; Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;component TodoItem({ todo, onToggle, onDelete }) {
  &amp;lt;li&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; checked={todo.completed} onChange={onToggle} /&amp;gt;
    &amp;lt;span class={todo.completed ? &quot;done&quot; : &quot;&quot;}&amp;gt;{todo.text}&amp;lt;/span&amp;gt;
    &amp;lt;button onClick={onDelete}&amp;gt;{&quot;×&quot;}&amp;lt;/button&amp;gt;
  &amp;lt;/li&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;App&lt;/code&gt; Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export component App() {
  const todos = #[];

  function add(text) {
    todos.push(#{ id: Date.now(), text, completed: false });
  }

  function toggle(t) {
    t.completed = !t.completed;
  }

  function remove(id) {
    const idx = todos.findIndex(t =&amp;gt; t.id === id);
    if (idx &amp;gt;= 0) todos.splice(idx, 1);
  }

  const remaining = () =&amp;gt; todos.filter(t =&amp;gt; !t.completed).length;

  &amp;lt;div class=&quot;app&quot;&amp;gt;
    &amp;lt;h1&amp;gt;{&quot;Todo List&quot;}&amp;lt;/h1&amp;gt;

    &amp;lt;TodoInput onAdd={add} /&amp;gt;

    &amp;lt;ul&amp;gt;
      for (const t of todos) {
        &amp;lt;TodoItem
          todo={t}
          onToggle={() =&amp;gt; toggle(t)}
          onDelete={() =&amp;gt; remove(t.id)}
        /&amp;gt;
      }
    &amp;lt;/ul&amp;gt;

    &amp;lt;p&amp;gt;{todos.length}{&quot; total / &quot;}{remaining()}{&quot; remaining&quot;}&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Framework Comparison&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Ripple&lt;/th&gt;
&lt;th&gt;React&lt;/th&gt;
&lt;th&gt;Vue 3&lt;/th&gt;
&lt;th&gt;Svelte&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State model&lt;/td&gt;
&lt;td&gt;&lt;code&gt;track()&lt;/code&gt; + &lt;code&gt;@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hooks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ref()&lt;/code&gt; / reactive&lt;/td&gt;
&lt;td&gt;Stores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOM updates&lt;/td&gt;
&lt;td&gt;Fine-grained&lt;/td&gt;
&lt;td&gt;VDOM diff&lt;/td&gt;
&lt;td&gt;VDOM diff&lt;/td&gt;
&lt;td&gt;Compile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boilerplate&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;Scoped&lt;/td&gt;
&lt;td&gt;Modules&lt;/td&gt;
&lt;td&gt;SFC Scoped&lt;/td&gt;
&lt;td&gt;Scoped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-friendly&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime size&lt;/td&gt;
&lt;td&gt;Small&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Tiny&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Who Should Use Ripple?&lt;/h2&gt;
&lt;p&gt;Ripple is ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI-assisted codebases&lt;/li&gt;
&lt;li&gt;dashboards &amp;amp; realtime UIs&lt;/li&gt;
&lt;li&gt;enterprise maintainability&lt;/li&gt;
&lt;li&gt;mobile/web hybrid UIs&lt;/li&gt;
&lt;li&gt;developers who dislike overengineering&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Official Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ripplejs.com&quot;&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Ripple-TS/ripple&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;If React gave us JSX, Vue gave us SFCs, and Svelte gave us compilation, Ripple asks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“What if UI could be reactive without ceremony?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And it answers convincingly.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/bun-workflows/&quot;&gt;Mastering Bun for Maximum Developer Productivity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/complete-monorepo-guide/&quot;&gt;Mastering Modern Monorepo Development with pnpm, Workspaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/files-dirs-nodejs/&quot;&gt;Get All Files and Folders in Node.js Directories&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Clean React Architecture for Sustainable Front-End Development</title><link>https://jsdev.space/maintainable-react-code/</link><guid isPermaLink="true">https://jsdev.space/maintainable-react-code/</guid><description>Learn practical techniques for writing cleaner and more maintainable React code. Covers component boundaries, utilities, conditions, data access, naming, and more.</description><pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React projects tend to live much longer than developers expect. Features evolve, products pivot, team members change, and soon a “quick MVP” becomes a multi-year codebase. In that reality, the winning mindset isn’t “ship the UI fast”, but organize UI so future developers can ship fast too.&lt;/p&gt;
&lt;p&gt;Clean code in React is not about pedantic rules or academic purity. It&apos;s about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;predictable component structure&lt;/li&gt;
&lt;li&gt;transparent data flow&lt;/li&gt;
&lt;li&gt;minimal hidden assumptions&lt;/li&gt;
&lt;li&gt;separation of concerns&lt;/li&gt;
&lt;li&gt;reusable behavior and presentation layers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this extended guide, we’ll explore real-world patterns that keep React components clean, readable, and maintainable at scale — with improved examples, new entities, and modern TypeScript-friendly patterns.&lt;/p&gt;
&lt;h2&gt;1. Extracting List Rendering Into Dedicated Components&lt;/h2&gt;
&lt;p&gt;A common early smell: components that mix business logic, screen-level state, and large .map() rendering blocks.&lt;/p&gt;
&lt;h3&gt;Overloaded Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function ProjectTabsPanel(props) {
  const {
    projectsArray,
    selectedProjectId,
    onProjectSelect,
    filtersArray,
    selectedFilterId,
    onFilterSelect,
  } = props;

  const hasProjects = projectsArray.length &amp;gt; 0;
  const hasFilters = filtersArray.length &amp;gt; 0;

  if (!hasProjects &amp;amp;&amp;amp; !hasFilters) {
    return null;
  }

  return (
    &amp;lt;div className=&quot;panel&quot;&amp;gt;
      {hasProjects &amp;amp;&amp;amp; (
        &amp;lt;div className=&quot;project-section&quot;&amp;gt;
          {projectsArray.map((item) =&amp;gt; {
            const isChosen = item.id === selectedProjectId;
            return (
              &amp;lt;button
                key={item.id}
                className={isChosen ? &quot;btn active&quot; : &quot;btn&quot;}
                onClick={() =&amp;gt; onProjectSelect(item.id)}
              &amp;gt;
                {item.label}
              &amp;lt;/button&amp;gt;
            );
          })}
        &amp;lt;/div&amp;gt;
      )}

      {hasFilters &amp;amp;&amp;amp; (
        &amp;lt;div className=&quot;filter-section&quot;&amp;gt;
          {/* Filter rendering */}
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Refactored List Component&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;type ProjectListProps = {
  items: Array&amp;lt;{ id: string; label: string }&amp;gt;;
  activeId: string | null;
  onSelect: (id: string) =&amp;gt; void;
};

function ProjectTabsList({ items, activeId, onSelect }: ProjectListProps) {
  return (
    &amp;lt;&amp;gt;
      {items.map((entry) =&amp;gt; {
        const isActive = entry.id === activeId;
        return (
          &amp;lt;button
            key={entry.id}
            className={isActive ? &quot;btn active&quot; : &quot;btn&quot;}
            onClick={() =&amp;gt; onSelect(entry.id)}
          &amp;gt;
            {entry.label}
          &amp;lt;/button&amp;gt;
        );
      })}
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Moving Helper Functions Outside of Components&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;export function TimestampText({ value }) {
  const normalize = (ts: string) =&amp;gt;
    new Date(ts).toLocaleString(&quot;en-US&quot;, { dateStyle: &quot;medium&quot; });

  return &amp;lt;span&amp;gt;{normalize(value)}&amp;lt;/span&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Better Version&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export const US_DATE_FORMAT = new Intl.DateTimeFormat(&quot;en-US&quot;, {
  dateStyle: &quot;medium&quot;,
  timeStyle: &quot;short&quot;,
});

export function formatTimestamp(input: string): string {
  return US_DATE_FORMAT.format(new Date(input));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;export function TimestampText({ value }: { value: string }) {
  return &amp;lt;span&amp;gt;{formatTimestamp(value)}&amp;lt;/span&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Destructuring Props Explicitly&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;function ProfileCard({ fullName, years }: { fullName: string; years: number }) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;p&amp;gt;{fullName}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;{years}&amp;lt;/p&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. Extracting Complex Conditions Into Named Constants&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  const shouldIgnoreScroll =
    firstLoad &amp;amp;&amp;amp; messages.length === 0 &amp;amp;&amp;amp; newArrived;

  if (shouldIgnoreScroll) return;

  scrollDown();
}, [firstLoad, messages.length, newArrived]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. Collapsing Deep Property Access&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;function StatsValue({ record }: { record: any }) {
  const stats = record?.details?.stats;
  const value = stats?.value ?? &quot;N/A&quot;;

  return &amp;lt;div&amp;gt;{value}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. Avoiding Magic Numbers&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;const BONUS_SCORE_THRESHOLD = 200;
const BONUS_MULTIPLIER = 1.1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Writing clean React code is about creating systems that survive time and team growth. By separating responsibilities, extracting logic, naming conditions, and removing magic values, you make code easier to maintain, onboard, and refactor. Clean code reduces cognitive load and speeds up development — today and years from now.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/10-custom-react-hooks/&quot;&gt;10 Must-Know Custom React Hooks for Your Projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/25-react-tips/&quot;&gt;25 React Tips to Boost Performance and Quality&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/chakra-ui-guide/&quot;&gt;Build React UIs Faster with Chakra UI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Friday Links #33:  Modern JavaScript Picks &amp; Highlights</title><link>https://jsdev.space/friday/friday-33/</link><guid isPermaLink="true">https://jsdev.space/friday/friday-33/</guid><description>Stay current with JavaScript trends, tools, and releases. This edition highlights new libraries, ecosystem updates, and useful resources for modern web developers.</description><pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaScript moves fast — and staying up to date can feel overwhelming. Each week we curate the most relevant updates, new libraries, and ecosystem changes so you don’t have to dig through 50 tabs and timelines. From framework releases to niche tooling improvements, this collection is designed to give developers a quick pulse check on what matters right now.&lt;/p&gt;
&lt;p&gt;Welcome to edition #33 of Friday Links — your weekly window into the ever-evolving JavaScript ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/friday-33.png&quot; alt=&quot;Friday Links #33&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;OpenAI Launches ChatGPT Health&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./images/ChatGPT-Health.png&quot; alt=&quot;ChatGPT Health&quot; title=&quot;ChatGPT Health&quot; /&gt;&lt;/p&gt;
&lt;p&gt;OpenAI has introduced ChatGPT Health, a dedicated section inside ChatGPT focused entirely on personal health. It’s more than a themed chat — users can discuss symptoms, interpret lab results, track metrics over time, and get clear explanations of medical terms.&lt;/p&gt;
&lt;p&gt;A key feature is integration with health and fitness services. Users can connect Apple Health, MyFitnessPal, and similar apps so the AI can analyze sleep, activity, nutrition, and wellness trends. In the U.S., it can also sync with electronic health records for reviewing test results and medical history.&lt;/p&gt;
&lt;p&gt;Privacy is a major emphasis: ChatGPT Health uses separate infrastructure with layered encryption, and its data is not used to train base models or mixed with regular chats by default.&lt;/p&gt;
&lt;h2&gt;📜 Articles &amp;amp; Tutorials&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://courses.csail.mit.edu/6.042/spring18/mcs.pdf&quot;&gt;Mathematics for Computer Science&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://webkit.org/blog/17660/introducing-css-grid-lanes/&quot;&gt;Introducing CSS Grid Lanes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.htmhell.dev/adventcalendar/2025/27/&quot;&gt;Replacing JS with just HTML&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.logrocket.com/react2shell-exploit/&quot;&gt;React2Shell exploit: What happened and lessons learned&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/devops/github-copilot-for-azure-boards/&quot;&gt;Azure Boards integration with GitHub Copilot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.stefanjudis.com/today-i-learned/load-env-files-in-node-js-scripts/&quot;&gt;Automatically load .env files in Node.js scripts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://krasimirtsonev.com/blog/article/streaming-json-in-just-200-lines-of-javascript&quot;&gt;Streaming JSON in just 200 lines of JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://openwebf.com/en/blog/announcing-webf&quot;&gt;Introducing WebF Beta: Bring JavaScript and the Web dev to Flutter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vadimdemedes/ink&quot;&gt;Ink 6.6&lt;/a&gt; — a library for building CLI apps with React, used by Claude Code, Gemini CLI, and others.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/olmo-3-allen-ai-open-source-llm&quot;&gt;Olmo 3: Fully Open-Source LLM from AI2 (Models, Data, &amp;amp; Code)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://railsdesigner.com/dialog-turboframe/&quot;&gt;Use native dialog with Turbo (and no extra JavaScript)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.datadoghq.com/blog/datadog-database-research/&quot;&gt;How microservice architectures have shaped the usage of database technologies&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://marmelab.com/blog/2025/12/04/typescript-type-as-a-programming-language.html&quot;&gt;TypeScript Types as a Programming Language&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;⚒️ Tools&lt;/h2&gt;
&lt;p&gt;A new free open-source service called &lt;a href=&quot;https://github.com/stamparm/maltrail&quot;&gt;Maltrail&lt;/a&gt; has been released for analyzing inbound and outbound network traffic and detecting malware. The project can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;detect malicious domains, URLs, and IP addresses&lt;/li&gt;
&lt;li&gt;identify harmful HTTP User-Agent strings&lt;/li&gt;
&lt;li&gt;spot modern attack tools on workstations&lt;/li&gt;
&lt;li&gt;provide strong network security without complex setup — installable in one click&lt;/li&gt;
&lt;li&gt;help reveal viruses, miners, and other unwanted network-active software&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://npmgraph.js.org/&quot;&gt;npmgraph&lt;/a&gt; is a web-based tool that visualizes npm package dependencies. You can enter one or more package names (or upload a &lt;code&gt;package.json&lt;/code&gt;) to see how their dependency graphs intersect. It also supports coloring packages by different metrics (like number of maintainers) and exporting the result as an SVG.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.usebruno.com/&quot;&gt;Bruno&lt;/a&gt; - Bruno is a fully local and Git-native solution to accelerate and secure API work and collaboration&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/cloudcommunity/Free-Certifications&quot;&gt;Free Certifications&lt;/a&gt; - A curated list of free courses with certifications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/max-sixty/worktrunk&quot;&gt;worktrunk&lt;/a&gt; - A CLI tool to manage multiple worktrees in Git repositories.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/gibbok/typescript-book&quot;&gt;The Concise TypeScript Book&lt;/a&gt; - A concise and practical guide to TypeScript, covering essential concepts and features.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/VibiumDev/vibium&quot;&gt;Vibium&lt;/a&gt; - Browser automation for AI agents and humans.&lt;/p&gt;
&lt;h2&gt;📚 Libs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/C4illin/ConvertX&quot;&gt;ConvertX&lt;/a&gt; - Self-hosted online file converter. Supports 1000+ formats.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fabricjs.com/&quot;&gt;Fabric.js 7&lt;/a&gt; - is a JavaScript library for working with the HTML5 canvas. It runs in both browsers and Node (via node-canvas) and provides an object model for canvas elements along with SVG-to-canvas and canvas-to-SVG conversion. The project also offers many demos with full source code.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/parallax/jsPDF&quot;&gt;jsPDF&lt;/a&gt; -  Client-side JavaScript PDF generation for everyone.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jrouwe/JoltPhysics.js&quot;&gt;JoltPhysics.js&lt;/a&gt; - A JavaScript/WebAssembly port of the Jolt Physics engine for real-time physics simulation in web applications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/martijnversluis/ChordSheetJS&quot;&gt;ChordSheetJS&lt;/a&gt; -  A JavaScript library for parsing and formatting chords and chord sheets&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/playcanvas/model-viewer&quot;&gt;PlayCanvas Model Viewer&lt;/a&gt; - A JavaScript library for displaying 3D models in the browser using WebGL and WebXR.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/recharts/recharts&quot;&gt;recharts&lt;/a&gt; - A composable charting library built on React components and D3.js.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nats-io/nats.js&quot;&gt;nats.js&lt;/a&gt; -  JavaScript client for Node.js, Bun, Deno and browser for NATS, the cloud native messaging system&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/LironEr/bundlemon&quot;&gt;bundlemon&lt;/a&gt; - A tool to monitor and enforce bundle size budgets for JavaScript projects.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Lulzx/tinypdf&quot;&gt;tinypdf&lt;/a&gt; - A lightweight PDF manipulation library for Node.js and the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/BlueprintLabIO/markdown-ui&quot;&gt;Markdown UI&lt;/a&gt; - An open standard for rendering interactive widgets in plain Markdown.&lt;/p&gt;
&lt;h2&gt;⌚ Releases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pnpm.io/blog/releases/10.27&quot;&gt;pnpm 10.27 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/prisma/prisma/releases/tag/7.2.0&quot;&gt;Prisma 7.2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/denoland/deno/releases/tag/v2.6.4&quot;&gt;Deno 2.6.4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sindresorhus/file-type&quot;&gt;file-type 21.2&lt;/a&gt; - Detect the file type of a file, stream, or data&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/taoqf/node-html-parser&quot;&gt;Fast HTML Parser 7.0.2 Released&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://middy.js.org/docs/upgrade/6-7/&quot;&gt;Middy 7.0&lt;/a&gt; brings middleware to AWS Lambda for Node.js, now with Durable Functions support.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel/nft&quot;&gt;Node File Trace 1.2&lt;/a&gt; - determines the minimal set of files required for an app to execute.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://orange-orm.io/&quot;&gt;Orange ORM 4.8&lt;/a&gt; — an Object-Relational Mapper for Node, Bun, and Deno.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadashy/repomix/releases/tag/v1.11.0&quot;&gt;Repomix 1.11&lt;/a&gt; — package an entire repository into a single, LLM-friendly file.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bdeitte/hot-shots&quot;&gt;hot-shots 12.0 / 12.1&lt;/a&gt; — a Node.js client for statsd, DogStatsD, and Telegraf.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/color-js/color.js/releases/tag/v…</content:encoded></item><item><title>Building Maintainable React Forms With Primitives</title><link>https://jsdev.space/react-form-primitives/</link><guid isPermaLink="true">https://jsdev.space/react-form-primitives/</guid><description>Learn how to replace messy custom React forms with a structured, reusable system based on primitives and field components for consistency and scalability.</description><pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;“Not again… another form.”&lt;/p&gt;
&lt;p&gt;Working with forms in React is one of the most common frontend tasks — and one of the easiest places to accumulate technical debt. Before worrying about architecture, every developer has to understand the basics: how form events work, how values flow from inputs, and how React and TypeScript model those interactions.&lt;/p&gt;
&lt;p&gt;If you’re not fully confident in that layer, the guide - &lt;a href=&quot;https://jsdev.space/react-form-events-guide/&quot;&gt;React Form Events &amp;amp; TypeScript&lt;/a&gt; does an excellent job of breaking down &lt;code&gt;onChange&lt;/code&gt;, &lt;code&gt;onSubmit&lt;/code&gt;, &lt;code&gt;onBlur&lt;/code&gt;, &lt;code&gt;onFocus&lt;/code&gt;, and their TypeScript typings in a clear, practical way.&lt;/p&gt;
&lt;p&gt;However, even with a solid understanding of form events, another problem quickly appears. Knowing &lt;em&gt;how&lt;/em&gt; forms work doesn’t automatically tell you &lt;em&gt;how to structure them&lt;/em&gt; once your application grows. A simple form with a few inputs is easy to manage, but as soon as you have 10–20 fields, shared validation rules, server errors, and multiple screens using the same inputs, ad‑hoc solutions stop scaling.&lt;/p&gt;
&lt;p&gt;This article focuses on that next layer of complexity. Instead of discussing events and handlers, we’ll look at how to design a &lt;strong&gt;predictable, reusable form system&lt;/strong&gt; built on four ideas:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;UI primitives&lt;/strong&gt; — small, “dumb” components responsible only for appearance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Cell wrapper&lt;/strong&gt; — a single place for labels, hints, and error messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Field components&lt;/strong&gt; — thin adapters between React Hook Form and your UI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schemas&lt;/strong&gt; — one source of truth for validation and TypeScript types.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Together, these layers help turn forms from a recurring source of chaos into a solved, scalable part of your UI architecture.&lt;/p&gt;
&lt;h2&gt;The Scaling Problem: “Toy Form” vs “Product Form”&lt;/h2&gt;
&lt;p&gt;A typical first form is built with local state and a submit handler. It works — until it doesn’t.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as React from &quot;react&quot;;

export function SignInToy() {
  const [email, setEmail] = React.useState(&quot;&quot;);
  const [secret, setSecret] = React.useState(&quot;&quot;);
  const [issues, setIssues] = React.useState&amp;lt;{ email?: string; secret?: string }&amp;gt;(
    {}
  );

  const onSubmit = (e: React.FormEvent) =&amp;gt; {
    e.preventDefault();

    const next: typeof issues = {};
    if (!email.includes(&quot;@&quot;)) next.email = &quot;Please enter a valid email.&quot;;
    if (secret.trim().length &amp;lt; 6) next.secret = &quot;Minimum 6 characters.&quot;;
    setIssues(next);

    if (Object.keys(next).length === 0) {
      // send request...
    }
  };

  return (
    &amp;lt;form onSubmit={onSubmit}&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;label&amp;gt;Email&amp;lt;/label&amp;gt;
        &amp;lt;input value={email} onChange={(e) =&amp;gt; setEmail(e.target.value)} /&amp;gt;
        {issues.email &amp;amp;&amp;amp; &amp;lt;div&amp;gt;{issues.email}&amp;lt;/div&amp;gt;}
      &amp;lt;/div&amp;gt;

      &amp;lt;div&amp;gt;
        &amp;lt;label&amp;gt;Password&amp;lt;/label&amp;gt;
        &amp;lt;input
          type=&quot;password&quot;
          value={secret}
          onChange={(e) =&amp;gt; setSecret(e.target.value)}
        /&amp;gt;
        {issues.secret &amp;amp;&amp;amp; &amp;lt;div&amp;gt;{issues.secret}&amp;lt;/div&amp;gt;}
      &amp;lt;/div&amp;gt;

      &amp;lt;button type=&quot;submit&quot;&amp;gt;Sign in&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine this pattern multiplied across a large application. More fields mean more state, more branching validation logic, and more duplicated markup. At that point, reviewing, testing, and maintaining forms becomes disproportionately expensive.&lt;/p&gt;
&lt;h2&gt;Form Libraries Help Logic, Not UI&lt;/h2&gt;
&lt;p&gt;Libraries like React Hook Form reduce boilerplate and improve performance, but they mainly solve &lt;strong&gt;state management and validation wiring&lt;/strong&gt;. They don’t define how your fields should look, how errors are rendered, or how consistency is enforced across the UI.&lt;/p&gt;
&lt;p&gt;That missing layer is where most form complexity actually lives.&lt;/p&gt;
&lt;h2&gt;The Structured Approach&lt;/h2&gt;
&lt;p&gt;The solution is to separate concerns clearly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primitives&lt;/strong&gt; define how inputs look and behave at the lowest level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cell&lt;/strong&gt; defines how a “field” is presented: label, hint, error.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Field components&lt;/strong&gt; connect form state to UI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schemas&lt;/strong&gt; define validation and typing in one place.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This separation keeps each piece small and understandable — and makes the whole system easier to scale.&lt;/p&gt;
&lt;h2&gt;Step 1: UI Primitives&lt;/h2&gt;
&lt;p&gt;Primitives are intentionally boring. They know nothing about forms or validation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as React from &quot;react&quot;;

export const TextInput = React.forwardRef&amp;lt;
  HTMLInputElement,
  React.InputHTMLAttributes&amp;lt;HTMLInputElement&amp;gt;
&amp;gt;(function TextInput(props, ref) {
  return &amp;lt;input ref={ref} {...props} className=&quot;input&quot; /&amp;gt;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because primitives are generic, you can reuse them outside forms — in filters, search bars, or settings panels.&lt;/p&gt;
&lt;h2&gt;Step 2: The Cell Wrapper&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Cell&lt;/code&gt; component standardizes layout and error display.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as React from &quot;react&quot;;

type CellProps = {
  label?: string;
  hint?: React.ReactNode;
  error?: string;
  children: React.ReactNode;
};

export function Cell({ label, hint, error, children }: CellProps) {
  return (
    &amp;lt;div className=&quot;cell&quot;&amp;gt;
      {label &amp;amp;&amp;amp; &amp;lt;label className=&quot;cell__label&quot;&amp;gt;{label}&amp;lt;/label&amp;gt;}
      &amp;lt;div className=&quot;cell__control&quot;&amp;gt;{children}&amp;lt;/div&amp;gt;
      {hint &amp;amp;&amp;amp; &amp;lt;div className=&quot;cell__hint&quot;&amp;gt;{hint}&amp;lt;/div&amp;gt;}
      {error &amp;amp;&amp;amp; &amp;lt;div className=&quot;cell__error&quot;&amp;gt;{error}&amp;lt;/div&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All visual consistency flows through this component.&lt;/p&gt;
&lt;h2&gt;Step 3: Field Components&lt;/h2&gt;
&lt;p&gt;Field components glue React Hook Form to your UI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useFormContext } from &quot;react-hook-form&quot;;

export function TextField({ name, label, ...props }) {
  const {
    register,
    formState: { errors },
  } = useFormContext();

  return (
    &amp;lt;Cell label={label} error={errors[name]?.message}&amp;gt;
      &amp;lt;TextInput {...register(name)} {...props} /&amp;gt;
    &amp;lt;/Cell&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each field type lives in its own small file, making behavior explicit and predictable.&lt;/p&gt;
&lt;h2&gt;Step 4: Schemas and Validation&lt;/h2&gt;
&lt;p&gt;Schemas (for example, with Zod) give you a single source of truth.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { z } from &quot;zod&quot;;

export const ProfileSchema = z.object({
  email: z.string().email(&quot;Invalid email&quot;),
  password: z.string().min(6, &quot;Minimum 6 characters&quot;),
});

export type ProfileValues = z.infer&amp;lt;typeof ProfileSchema&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Validation rules and TypeScript types stay in sync.&lt;/p&gt;
&lt;h2&gt;Step 5: Assembling a Form&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { FormProvider, useForm } from &quot;react-hook-form&quot;;
import { zodResolver } from &quot;@hookform/resolvers/zod&quot;;

export function ProfileForm() {
  const methods = useForm({
    resolver: zodResolver(ProfileSchema),
  });

  return (
    &amp;lt;FormProvider {...methods}&amp;gt;
      &amp;lt;form onSubmit={methods.handleSubmit(console.log)}&amp;gt;
        &amp;lt;TextField name=&quot;email&quot; label=&quot;Email&quot; /&amp;gt;
        &amp;lt;TextField name=&quot;password&quot; label=&quot;Password&quot; type=&quot;password&quot; /&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/FormProvider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The form reads like intent, not implementation details.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Understanding React form events is the foundation — but structure is what makes forms scale. By layering primitives, a shared Cell wrapper, and small field components on top of a form library, you get consistency, reuse, and clarity without sacrificing flexibility.&lt;/p&gt;
&lt;p&gt;Once this system is in place, forms stop being a special problem and become just another predictable part of your UI.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/10-custo…</content:encoded></item><item><title>How to Use the never Type for Real-World Error Handling</title><link>https://jsdev.space/howto/typescript-never-error-handling-guide/</link><guid isPermaLink="true">https://jsdev.space/howto/typescript-never-error-handling-guide/</guid><description>Learn how to use TypeScript’s never type to model impossible states, enforce exhaustive checks, and catch missing error cases before they reach production.</description><pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;code&gt;never&lt;/code&gt; type in TypeScript is often explained as “a function that never returns”.
While technically correct, this explanation is rarely useful in real-world codebases.&lt;/p&gt;
&lt;p&gt;In practice, &lt;code&gt;never&lt;/code&gt; becomes powerful when you want the compiler to fail loudly as your
application evolves — especially when handling errors, exhaustive checks, and states
that should be impossible but tend to appear over time.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll look at how &lt;code&gt;never&lt;/code&gt; behaves in production code, why it is easy
to misuse, and how it helps catch entire classes of bugs before they reach runtime.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What &lt;code&gt;never&lt;/code&gt; Really Means&lt;/h2&gt;
&lt;p&gt;The TypeScript type system models types as &lt;strong&gt;sets of values&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;number&lt;/code&gt; is the set of all possible numbers&lt;/li&gt;
&lt;li&gt;&lt;code&gt;boolean&lt;/code&gt; is the set &lt;code&gt;{true, false}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;string literal types like &lt;code&gt;&quot;ready&quot;&lt;/code&gt; are a set with a single element&lt;/li&gt;
&lt;li&gt;&lt;code&gt;never&lt;/code&gt; is the &lt;strong&gt;empty set&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because &lt;code&gt;never&lt;/code&gt; has &lt;em&gt;no possible values&lt;/em&gt;, no actual value can ever belong to it. That’s why the following function is valid:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function triggerFailure(reason: string): never {
  throw new Error(reason);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript ensures that functions returning &lt;code&gt;never&lt;/code&gt; cannot complete normally.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const outcome = triggerFailure(&quot;Unexpected state!&quot;);
//    ^? never
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;outcome&lt;/code&gt; has type &lt;code&gt;never&lt;/code&gt;, because TypeScript knows the function cannot return.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Exhaustive error handling&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;type ApiResult =
  | { status: &quot;success&quot;; data: string }
  | { status: &quot;error&quot;; error: Error };

function assertNever(value: never): never {
  throw new Error(`Unhandled case: ${JSON.stringify(value)}`);
}

function handleResult(result: ApiResult) {
  switch (result.status) {
    case &quot;success&quot;:
      return result.data;

    case &quot;error&quot;:
      throw result.error;

    default:
      return assertNever(result);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, never ensures that every possible state of ApiResult is handled.
If a new status is added later, TypeScript will immediately report an error.&lt;/p&gt;
&lt;p&gt;This is where never becomes valuable — not as a theoretical type,
but as a guardrail for evolving codebases.&lt;/p&gt;
&lt;h2&gt;The Common Mistake: Using &lt;code&gt;never&lt;/code&gt; for Error Modeling&lt;/h2&gt;
&lt;p&gt;Developers sometimes attempt this pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function safeDivide(x: number, y: number): number | never {
  if (y === 0) {
    throw new Error(&quot;Division by zero!&quot;);
  }
  return x / y;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They expect the return type to reflect both “good” and “error” states.&lt;/p&gt;
&lt;p&gt;But TypeScript evaluates:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;number | never → number
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The empty set adds nothing to the union — &lt;code&gt;never&lt;/code&gt; collapses and becomes meaningless.&lt;/p&gt;
&lt;p&gt;This is why:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const result = safeDivide(10, 0);
// result: number
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code successfully compiles but is misleading.&lt;br /&gt;
The &lt;code&gt;never&lt;/code&gt; type &lt;strong&gt;should never be used to describe an error state&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why &lt;code&gt;never&lt;/code&gt; Collapses in Unions&lt;/h2&gt;
&lt;p&gt;Unions represent the &lt;strong&gt;set‑theoretic union&lt;/strong&gt; of their members.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(number-set) ∪ (empty-set) = number-set
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not a TypeScript quirk — it is mathematically correct.&lt;/p&gt;
&lt;p&gt;Thus:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;string | never&lt;/code&gt; → &lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;boolean | never&lt;/code&gt; → &lt;code&gt;boolean&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;T | never&lt;/code&gt; → &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;never&lt;/code&gt; disappears because it cannot contribute any valid value.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Correct Use of &lt;code&gt;never&lt;/code&gt;: Exhaustiveness Checking&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;never&lt;/code&gt; becomes powerful when used to model &lt;strong&gt;impossible states&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Step 1 — Create a discriminated union&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;type CircleShape = { kind: &quot;circle&quot;; radius: number };
type SquareShape = { kind: &quot;square&quot;; side: number };
type RectShape = { kind: &quot;rectangle&quot;; width: number; height: number };

type ShapeEntity = CircleShape | SquareShape | RectShape;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2 — Write an exhaustiveness checker&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function assertImpossible(value: never): never {
  throw new Error(&quot;Encountered an impossible case: &quot; + JSON.stringify(value));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3 — Use it inside a switch&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export function computeArea(geom: ShapeEntity): number {
  switch (geom.kind) {
    case &quot;circle&quot;:
      return Math.PI * geom.radius ** 2;

    case &quot;square&quot;:
      return geom.side ** 2;

    case &quot;rectangle&quot;:
      return geom.height * geom.width;

    default:
      return assertImpossible(geom);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If someone later updates:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type TriangleShape = { kind: &quot;triangle&quot;; a: number; b: number; c: number };
type ShapeEntity = CircleShape | SquareShape | RectShape | TriangleShape;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler immediately screams:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Argument of type &apos;TriangleShape&apos; is not assignable to parameter of type &apos;never&apos;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly how &lt;code&gt;never&lt;/code&gt; should be used:&lt;br /&gt;
catching missing branches&lt;br /&gt;
preventing silent logic failures&lt;br /&gt;
enforcing total coverage of all cases&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Better Model for Error Handling: The Result Pattern&lt;/h2&gt;
&lt;p&gt;Instead of misusing &lt;code&gt;never&lt;/code&gt;, model error states explicitly using a discriminated union:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type FailureInfo = { status: &quot;fail&quot;; message: string };
type SuccessInfo&amp;lt;T&amp;gt; = { status: &quot;ok&quot;; data: T };

type ResultBox&amp;lt;T&amp;gt; = FailureInfo | SuccessInfo&amp;lt;T&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Helper creators:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const createFailure = (msg: string): FailureInfo =&amp;gt; ({
  status: &quot;fail&quot;,
  message: msg,
});

const createSuccess = &amp;lt;T&amp;gt;(value: T): SuccessInfo&amp;lt;T&amp;gt; =&amp;gt; ({
  status: &quot;ok&quot;,
  data: value,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A safe division using proper error modeling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function robustDivide(n1: number, n2: number): ResultBox&amp;lt;number&amp;gt; {
  if (n2 === 0) return createFailure(&quot;Division by zero&quot;);
  return createSuccess(n1 / n2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const output = robustDivide(8, 0);

if (output.status === &quot;fail&quot;) {
  console.error(&quot;Error:&quot;, output.message);
} else {
  console.log(&quot;Result:&quot;, output.data);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is explicit, predictable, and fully typed.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Wrapping Unsafe Functions: A &lt;code&gt;try&lt;/code&gt;-Safe Wrapper&lt;/h2&gt;
&lt;p&gt;Sometimes an existing function throws. Wrap it safely:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function handleSafely&amp;lt;Args extends unknown[], Ret&amp;gt;(
  fn: (...p: Args) =&amp;gt; Ret,
  ...inputs: Args
): ResultBox&amp;lt;Ret&amp;gt; {
  try {
    return createSuccess(fn(...inputs));
  } catch (err: any) {
    return createFailure(err?.message ?? &quot;Unknown failure&quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function riskyDivide(a: number, b: number) {
  if (b === 0) throw new Error(&quot;Boom!&quot;);
  return a / b;
}

const checked = handleSafely(riskyDivide, 10, 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Converting ResultBox to a Throwing Function&lt;/h2&gt;
&lt;p&gt;If needed, convert the safe result back into a throwing workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function unwrapOrCrash&amp;lt;T&amp;gt;(supplier: () =&amp;gt; ResultBox&amp;lt;T&amp;gt;): T {
  const outcome = supplier();
  if (outcome.status === &quot;ok&quot;) return outcome.data;
  throw new Error(outcome.message);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const finalAnswer = unwrapOrCrash(() =&amp;gt; robustDivide(10, 2));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why &lt;code&gt;never&lt;/code&gt; Often Fails in Real Projects&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;never&lt;/code&gt; does not automatically make code safer.&lt;/p&gt;
&lt;p&gt;Common mistakes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;assuming &lt;code&gt;never&lt;/code&gt; replaces runtime validation&lt;/li&gt;
&lt;li&gt;overusing it in public APIs&lt;/li&gt;
&lt;li&gt;hiding real errors behind “impossible” states&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In production, &lt;code&gt;never&lt;/code&gt; works best when combined with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;discriminated …</content:encoded></item><item><title>Chakra UI: A Complete Guide to Faster, Cleaner, and Accessible UI</title><link>https://jsdev.space/chakra-ui-guide/</link><guid isPermaLink="true">https://jsdev.space/chakra-ui-guide/</guid><description>A practical deep-dive into Chakra UI v3: component styling, theming, accessibility, performance, responsive design, and modern React examples.</description><pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In day‑to‑day frontend work, we keep solving the same problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;building consistent buttons, forms, layouts, and modals&lt;/li&gt;
&lt;li&gt;making everything responsive on phones, tablets, and desktops&lt;/li&gt;
&lt;li&gt;keeping colors, typography, and spacing uniform across the app&lt;/li&gt;
&lt;li&gt;implementing accessibility and keyboard navigation correctly&lt;/li&gt;
&lt;li&gt;fighting with ever‑growing CSS files and design drift&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Writing CSS from scratch for every new page quickly turns into a chore. That’s exactly the type of repetitive work &lt;strong&gt;Chakra UI&lt;/strong&gt; removes from your life.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll look at how &lt;strong&gt;Chakra UI v3&lt;/strong&gt; paired with &lt;strong&gt;React&lt;/strong&gt; helps you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stop hand‑writing CSS for every component&lt;/li&gt;
&lt;li&gt;keep a single source of truth for design tokens&lt;/li&gt;
&lt;li&gt;ship accessible UI without memorizing every WAI‑ARIA rule&lt;/li&gt;
&lt;li&gt;build responsive layouts without writing &lt;code&gt;@media&lt;/code&gt; queries&lt;/li&gt;
&lt;li&gt;keep performance under control with tree‑shaking and minimal styling runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Along the way we’ll use improved code samples, renamed variables, and modern patterns.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Official docs: &lt;a href=&quot;https://chakra-ui.com/&quot;&gt;chakra docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Styling Libraries Exist at All&lt;/h2&gt;
&lt;p&gt;Let’s be honest: vanilla CSS is powerful, but it doesn’t scale nicely in large React apps.&lt;/p&gt;
&lt;p&gt;Typical problems when you style everything manually:&lt;/p&gt;
&lt;h3&gt;1. Slowness&lt;/h3&gt;
&lt;p&gt;You might spend &lt;strong&gt;hours&lt;/strong&gt; on details like hover states, focus rings, input errors, spacing between elements, and layout quirks.&lt;/p&gt;
&lt;h3&gt;2. Visual Inconsistency&lt;/h3&gt;
&lt;p&gt;You start with a clean design, but six months later:&lt;br /&gt;
different border radii, slightly different blues, spacing that &quot;almost&quot; matches, and three button variations that should have been one.&lt;/p&gt;
&lt;h3&gt;3. Accessibility Debt&lt;/h3&gt;
&lt;p&gt;Screen readers, roles, ARIA attributes, focus management, ESC handling, keyboard navigation – all this is crucial, but hard to do correctly and consistently with plain HTML + CSS.&lt;/p&gt;
&lt;h3&gt;4. Bloated CSS&lt;/h3&gt;
&lt;p&gt;Legacy styles accumulate: old classes, unused helpers, and confusing overrides. Bundle size grows, and nobody wants to delete anything because &quot;it might be used somewhere&quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Component styling libraries&lt;/strong&gt; like Chakra UI solve these with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ready‑made building blocks (Button, Stack, Modal, Menu, etc.)&lt;/li&gt;
&lt;li&gt;theme tokens instead of magic values&lt;/li&gt;
&lt;li&gt;accessibility first approach&lt;/li&gt;
&lt;li&gt;built‑in responsiveness&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chakra takes this approach and makes it very ergonomic for React.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Styling with Props Instead of Raw CSS&lt;/h2&gt;
&lt;p&gt;In Chakra, &lt;strong&gt;style props&lt;/strong&gt; are the core idea:&lt;br /&gt;
you describe &lt;em&gt;what&lt;/em&gt; the component should look like directly in JSX instead of switching between JS and CSS files.&lt;/p&gt;
&lt;h3&gt;A basic button, Chakra style&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { Button } from &quot;@chakra-ui/react&quot;

export function SendMessageButton() {
  return (
    &amp;lt;Button
      colorScheme=&quot;teal&quot;
      size=&quot;lg&quot;
      borderRadius=&quot;xl&quot;
      boxShadow=&quot;md&quot;
      _hover={{
        boxShadow: &quot;xl&quot;,
        transform: &quot;translateY(-1px)&quot;,
      }}
    &amp;gt;
      Send message
    &amp;lt;/Button&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./images/chakra-btn.png&quot; alt=&quot;Chakra UI button example&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This one component includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;colors&lt;/li&gt;
&lt;li&gt;size&lt;/li&gt;
&lt;li&gt;border radius&lt;/li&gt;
&lt;li&gt;shadow&lt;/li&gt;
&lt;li&gt;hover state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No separate CSS file, no BEM class names, no &lt;code&gt;:hover&lt;/code&gt; selectors.&lt;/p&gt;
&lt;h3&gt;Equivalent CSS for comparison&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;.primary-button {
  background-color: #319795;
  color: #fff;
  padding: 0.75rem 1.5rem;
  border-radius: 0.75rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, .1);
  transition: all 0.15s ease-out;
}

.primary-button:hover {
  box-shadow: 0 10px 15px rgba(0, 0, 0, .15);
  transform: translateY(-1px);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;export function SendMessageButtonRaw() {
  return &amp;lt;button className=&quot;primary-button&quot;&amp;gt;Send message&amp;lt;/button&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both work, but the Chakra version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;keeps markup and styles in one place&lt;/li&gt;
&lt;li&gt;is more discoverable (props are auto‑completed in your IDE)&lt;/li&gt;
&lt;li&gt;plays nicely with dynamic values coming from state or props&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://chakra-ui.com/docs/styling/overview&quot;&gt;Style props reference&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. A Single Theme Controls the Whole App&lt;/h2&gt;
&lt;p&gt;Instead of scattering values like &lt;code&gt;#319795&lt;/code&gt; and &lt;code&gt;1.5rem&lt;/code&gt; across components, Chakra encourages you to put them into a &lt;strong&gt;theme&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can extend the default theme or create your own system.&lt;/p&gt;
&lt;h3&gt;Minimal theme setup with design tokens&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import {
  ChakraProvider,
  createSystem,
  defaultConfig,
  defineConfig,
} from &quot;@chakra-ui/react&quot;

const designConfig = defineConfig({
  theme: {
    tokens: {
      colors: {
        accent: { value: &quot;#6ED209&quot; },
        surface: { value: &quot;#F8FFF2&quot; },
      },
      radii: {
        pill: { value: &quot;999px&quot; },
      },
    },
  },
})

const designSystem = createSystem(defaultConfig, designConfig)

export function RootApp({ children }: { children: React.ReactNode }) {
  return &amp;lt;ChakraProvider value={designSystem}&amp;gt;{children}&amp;lt;/ChakraProvider&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can use &lt;code&gt;accent&lt;/code&gt; and &lt;code&gt;surface&lt;/code&gt; anywhere:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Box, Button } from &quot;@chakra-ui/react&quot;

export function AccentCard() {
  return (
    &amp;lt;Box bg=&quot;surface&quot; p={6} borderRadius=&quot;lg&quot;&amp;gt;
      &amp;lt;Button bg=&quot;accent&quot; borderRadius=&quot;pill&quot;&amp;gt;
        Accent action
      &amp;lt;/Button&amp;gt;
    &amp;lt;/Box&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change &lt;code&gt;accent&lt;/code&gt; in one place → entire app updates.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://chakra-ui.com/docs/theming/overview&quot;&gt;Theming guide&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Extracting theme configuration into its own file&lt;/h3&gt;
&lt;p&gt;A common pattern is to keep all theme logic in something like &lt;code&gt;theme/system.ts&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// theme/system.ts
import { defineConfig, createSystem, defaultConfig } from &quot;@chakra-ui/react&quot;

const designConfig = defineConfig({
  globalCss: {
    &quot;html, body&quot;: {
      margin: 0,
      padding: 0,
      fontFamily: &quot;system-ui, -apple-system, BlinkMacSystemFont, sans-serif&quot;,
      scrollBehavior: &quot;smooth&quot;,
    },
  },
  theme: {
    tokens: {
      colors: {
        &quot;ink-strong&quot;: { value: &quot;#1A202C&quot; },
        &quot;ink-soft&quot;: { value: &quot;#718096&quot; },
        &quot;paper&quot;: { value: &quot;#F7FAFC&quot; },
      },
    },
    semanticTokens: {
      colors: {
        appBackground: {
          value: {
            base: &quot;{colors.paper}&quot;,
            _dark: &quot;#1A202C&quot;,
          },
        },
        appText: {
          value: {
            base: &quot;{colors.ink-strong}&quot;,
            _dark: &quot;{colors.paper}&quot;,
          },
        },
      },
    },
  },
})

export const designSystem = createSystem(defaultConfig, designConfig)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your entry point:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// app/providers.tsx
import { ChakraProvider } from &quot;@chakra-ui/react&quot;
import { designSystem } from &quot;@/theme/system&quot;

export function Providers({ children }: { children: React.ReactNode }) {
  return &amp;lt;ChakraProvider value={designSystem}&amp;gt;{children}&amp;lt;/ChakraProvider&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you &lt;strong&gt;one place&lt;/strong&gt; to tune your entire design language.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Accessibility Built In by Default&lt;/h2&gt;
&lt;p&gt;Manually making UI accessible means dealing with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;role&lt;/code&gt;, &lt;code&gt;aria-*&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;focus traps for modals&lt;/li&gt;
&lt;li&gt;escape handling&lt;/li&gt;
&lt;li&gt;tab order for menus&lt;/li&gt;
&lt;li&gt;keyboard navigation for lists and dialogs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-live&lt;/code&gt; for announcements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chakra UI bakes this into its components.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3.org/WAI/standards-guidelines/aria/&quot;&gt;WAI‑ARIA specs&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Accessible alert message&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { Alert, AlertIcon, AlertTitle, AlertDescription } from &quot;@c…</content:encoded></item><item><title>Mastering React Form Events with TypeScript</title><link>https://jsdev.space/react-form-events-guide/</link><guid isPermaLink="true">https://jsdev.space/react-form-events-guide/</guid><description>Learn how React form events work, how to type them in TypeScript, and explore improved examples: inputs, forms, buttons, keyboard events, validation, debouncing...</description><pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Working with forms is one of the most common tasks in React — and one of the most misunderstood. Beginners often struggle with event types, how to strongly‑type form handlers, how to extract values, and how to work with keyboard, mouse, focus, and form‑submission events correctly.&lt;/p&gt;
&lt;p&gt;This improved guide explains &lt;strong&gt;every major React form event&lt;/strong&gt;, adds &lt;strong&gt;better TypeScript examples&lt;/strong&gt;, and focuses heavily on &lt;strong&gt;real‑world patterns&lt;/strong&gt; like validation, debouncing, controlled inputs, preventing default behavior, and working with form data.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why React Form Events Matter&lt;/h2&gt;
&lt;p&gt;Any time a user:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;types into an input&lt;/li&gt;
&lt;li&gt;clicks a button&lt;/li&gt;
&lt;li&gt;submits a form&lt;/li&gt;
&lt;li&gt;focuses or blurs a field&lt;/li&gt;
&lt;li&gt;presses a key&lt;/li&gt;
&lt;li&gt;interacts with checkboxes, selects, radios, sliders&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;…React fires an event object you can react to.&lt;/p&gt;
&lt;p&gt;TypeScript helps us &lt;strong&gt;ensure correctness&lt;/strong&gt;, catch mistakes, and avoid bugs like undefined values or invalid event targets.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. &lt;code&gt;onChange&lt;/code&gt;: The Most Important Form Event&lt;/h2&gt;
&lt;h3&gt;TypeScript Type:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;onChange&lt;/code&gt; fires whenever the value of an input changes.&lt;/p&gt;
&lt;h3&gt;Improved Real‑World Example: Typing with Validation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export function UsernameField() {
  const [username, setUsername] = useState(&quot;&quot;);
  const [error, setError] = useState(&quot;&quot;);

  const handleUsername = (e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    const value = e.target.value;
    setUsername(value);

    if (value.length &amp;lt; 3) {
      setError(&quot;Username must be at least 3 characters.&quot;);
    } else {
      setError(&quot;&quot;);
    }
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;label&amp;gt;
        Username:
        &amp;lt;input type=&quot;text&quot; value={username} onChange={handleUsername} /&amp;gt;
      &amp;lt;/label&amp;gt;

      {error &amp;amp;&amp;amp; &amp;lt;p style={{ color: &quot;red&quot; }}&amp;gt;{error}&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;onSubmit&lt;/code&gt;: Handling Entire Form Submission&lt;/h2&gt;
&lt;h3&gt;TypeScript Type:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;React.FormEvent&amp;lt;HTMLFormElement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Improved Example with FormData Extraction&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export function LoginForm() {
  const [message, setMessage] = useState(&quot;&quot;);

  const handleSubmit = (e: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    e.preventDefault();

    const form = e.currentTarget;
    const data = new FormData(form);

    const email = data.get(&quot;email&quot;) as string;
    const password = data.get(&quot;password&quot;) as string;

    setMessage(`Submitted: ${email} / ${password}`);
  };

  return (
    &amp;lt;form onSubmit={handleSubmit}&amp;gt;
      &amp;lt;input name=&quot;email&quot; type=&quot;email&quot; placeholder=&quot;Email&quot; required /&amp;gt;
      &amp;lt;input name=&quot;password&quot; type=&quot;password&quot; placeholder=&quot;Password&quot; required /&amp;gt;
      &amp;lt;button type=&quot;submit&quot;&amp;gt;Log In&amp;lt;/button&amp;gt;
      &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;onFocus&lt;/code&gt;: Detecting When a User Enters a Field&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;React.FocusEvent&amp;lt;HTMLInputElement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example: Highlight active field&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export function FocusExample() {
  const [focused, setFocused] = useState(false);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input
        onFocus={() =&amp;gt; setFocused(true)}
        style={{ borderColor: focused ? &quot;dodgerblue&quot; : &quot;#ccc&quot; }}
      /&amp;gt;
      {focused &amp;amp;&amp;amp; &amp;lt;p&amp;gt;You&apos;re typing now!&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;onBlur&lt;/code&gt;: When the User Leaves a Field&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;React.FocusEvent&amp;lt;HTMLInputElement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example: Email validation&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export function EmailField() {
  const [error, setError] = useState(&quot;&quot;);

  const handleBlur = (e: React.FocusEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    const email = e.target.value;
    setError(email.includes(&quot;@&quot;) ? &quot;&quot; : &quot;Invalid email.&quot;);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input type=&quot;email&quot; onBlur={handleBlur} placeholder=&quot;Enter email&quot; /&amp;gt;
      {error &amp;amp;&amp;amp; &amp;lt;p style={{ color: &quot;red&quot; }}&amp;gt;{error}&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;onClick&lt;/code&gt;: Buttons &amp;amp; Interactions&lt;/h2&gt;
&lt;h3&gt;TypeScript Type:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;React.MouseEvent&amp;lt;HTMLButtonElement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example: Button loading state&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export function LoadingButton() {
  const [loading, setLoading] = useState(false);

  const handleClick = async () =&amp;gt; {
    setLoading(true);
    await new Promise(res =&amp;gt; setTimeout(res, 1000));
    setLoading(false);
  };

  return (
    &amp;lt;button onClick={handleClick} disabled={loading}&amp;gt;
      {loading ? &quot;Loading...&quot; : &quot;Click me&quot;}
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Keyboard Events&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;React.KeyboardEvent&amp;lt;HTMLInputElement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example: Submit on Enter&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export function SearchBox() {
  const [query, setQuery] = useState(&quot;&quot;);

  const handleKey = (e: React.KeyboardEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    if (e.key === &quot;Enter&quot;) {
      alert(`Searching for: ${query}`);
    }
  };

  return (
    &amp;lt;input
      value={query}
      onChange={(e) =&amp;gt; setQuery(e.target.value)}
      onKeyDown={handleKey}
      placeholder=&quot;Press Enter to search&quot;
    /&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Bonus: Debounced Input&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { useState, useEffect } from &quot;react&quot;;

export function DebouncedInput() {
  const [raw, setRaw] = useState(&quot;&quot;);
  const [debounced, setDebounced] = useState(&quot;&quot;);

  useEffect(() =&amp;gt; {
    const id = setTimeout(() =&amp;gt; setDebounced(raw), 400);
    return () =&amp;gt; clearTimeout(id);
  }, [raw]);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input
        onChange={(e) =&amp;gt; setRaw(e.target.value)}
        placeholder=&quot;Type slowly...&quot;
      /&amp;gt;
      &amp;lt;p&amp;gt;Debounced: {debounced}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;You now know how to handle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;change events&lt;/li&gt;
&lt;li&gt;submit events&lt;/li&gt;
&lt;li&gt;focus/blur&lt;/li&gt;
&lt;li&gt;click events&lt;/li&gt;
&lt;li&gt;keyboard events&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;debouncing&lt;/li&gt;
&lt;li&gt;extracting FormData&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;React + TypeScript becomes much easier once you understand event types.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/10-custom-react-hooks/&quot;&gt;10 Must-Know Custom React Hooks for Your Projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/25-react-tips/&quot;&gt;25 React Tips to Boost Performance and Quality&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/chakra-ui-guide/&quot;&gt;Build React UIs Faster with Chakra UI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Howto Fix the “Role Postgres Does Not Exist” Error in Docker</title><link>https://jsdev.space/howto/fix-postgres-role-error/</link><guid isPermaLink="true">https://jsdev.space/howto/fix-postgres-role-error/</guid><description>Learn how to fix the &quot;role &apos;postgres&apos; does not exist&quot; error when deploying a PostgreSQL Docker container, why it happens, and the correct solutions.</description><pubDate>Mon, 17 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When deploying a PostgreSQL instance inside a Docker container, one of the most common startup failures is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;psql: FATAL: role &quot;postgres&quot; does not exist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This error typically indicates that the initialization scripts did not create the default &lt;code&gt;postgres&lt;/code&gt; superuser. Below we explore the causes and the correct fixes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Why the Error Occurs&lt;/h2&gt;
&lt;h3&gt;1.1 Your data volume already has an old database&lt;/h3&gt;
&lt;p&gt;PostgreSQL only creates users when the data directory is empty.&lt;br /&gt;
If you reuse a volume, the &lt;code&gt;postgres&lt;/code&gt; role may simply not exist.&lt;/p&gt;
&lt;h3&gt;1.2 You changed the default POSTGRES_USER&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;POSTGRES_USER=myuser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes &lt;code&gt;myuser&lt;/code&gt; the superuser — not &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;1.3 Incorrect filesystem permissions&lt;/h3&gt;
&lt;p&gt;If PostgreSQL cannot write to &lt;code&gt;/var/lib/postgresql/data&lt;/code&gt;, initialization may silently fail.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Solutions&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;A. Delete the old volume (recommended)&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;docker compose down -v
docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;B. Explicitly set POSTGRES_USER&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;environment:
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: secret123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose down
docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;C. Manually create the missing role&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it &amp;lt;container&amp;gt; bash
psql -U myuser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside PostgreSQL:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE ROLE postgres WITH SUPERUSER LOGIN PASSWORD &apos;changeme&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;D. Fix permissions&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo chown -R 999:999 /path/to/volume
docker compose restart
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Always specify &lt;code&gt;POSTGRES_USER&lt;/code&gt; and &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Prefer &lt;em&gt;named volumes&lt;/em&gt; over bind mounts.&lt;/li&gt;
&lt;li&gt;Add healthchecks:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;healthcheck:
  test: [&quot;CMD-SHELL&quot;, &quot;pg_isready -U postgres&quot;]
  interval: 5s
  timeout: 3s
  retries: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Conclusion&lt;/h2&gt;
&lt;p&gt;This error happens because the container is using an already-initialized volume or the default superuser was changed. Resetting the volume, correcting your environment variables, or creating the role manually will resolve the problem.&lt;/p&gt;
&lt;p&gt;If you want extended troubleshooting for specific platforms (Railway, Render, Fly.io, Docker Swarm), I can add that too.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/howto/jaeger-docker-windows/&quot;&gt;Setting Up Jaeger with Docker on Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/postgresql-docker-compose/&quot;&gt;Setting Up Local PostgreSQL with Docker Compose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/add-months-to-date-js/&quot;&gt;Practical Month Arithmetic and Calendar Logic in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to Develop Accessible Interfaces with WAI-ARIA</title><link>https://jsdev.space/howto/wai-aria-accessible-ui/</link><guid isPermaLink="true">https://jsdev.space/howto/wai-aria-accessible-ui/</guid><description>A deep, practical guide to building accessible interfaces using WAI-ARIA roles, states, patterns, and proper keyboard support for modern web applications.</description><pubDate>Mon, 17 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern web applications have evolved into rich, desktop‑like interactive environments. Components such as modals, dropdown menus, accordions, tabs, and dynamically updated content are now standard. However, accessibility is often overlooked, leaving millions of users unable to interact with applications effectively. WAI‑ARIA provides a way to enrich custom UI components with semantic meaning and behavior for assistive technologies.&lt;/p&gt;
&lt;h2&gt;What WAI‑ARIA Is&lt;/h2&gt;
&lt;p&gt;WAI‑ARIA (Web Accessibility Initiative — Accessible Rich Internet Applications) is a specification created by the W3C to describe interactive interfaces to screen readers, keyboard users, and assistive devices. While HTML includes semantic elements like &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, developers frequently build UI using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, resulting in missing information for accessibility tools. ARIA fills in those gaps.&lt;/p&gt;
&lt;p&gt;ARIA includes three core building blocks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Roles&lt;/strong&gt; — define the purpose of an element&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;States &amp;amp; Properties&lt;/strong&gt; (&lt;code&gt;aria-*&lt;/code&gt;) — describe its current condition&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live Regions&lt;/strong&gt; — announce dynamic updates&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ARIA Roles&lt;/h2&gt;
&lt;p&gt;Roles communicate what an element represents.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;button&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;navigation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dialog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tab&lt;/code&gt;, &lt;code&gt;tablist&lt;/code&gt;, &lt;code&gt;tabpanel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;menu&lt;/code&gt;, &lt;code&gt;menuitem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;checkbox&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use native HTML elements whenever possible; use ARIA only when necessary.&lt;/p&gt;
&lt;h2&gt;ARIA Properties &amp;amp; States&lt;/h2&gt;
&lt;p&gt;These attributes explain how a UI element behaves and what its current status is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Properties&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aria-label&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-labelledby&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-describedby&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;States&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aria-expanded&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-selected&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-checked&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-disabled&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Live Regions&lt;/h2&gt;
&lt;p&gt;Live regions notify users of dynamic content updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aria-live=&quot;polite&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-live=&quot;assertive&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-atomic=&quot;true&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-relevant=&quot;additions text&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are essential for notifications, error messages, chat updates, and async content.&lt;/p&gt;
&lt;h2&gt;ARIA in SPA/React Environments&lt;/h2&gt;
&lt;p&gt;Frameworks like React sometimes re-render without changing DOM nodes, which can prevent screen readers from detecting updates. Use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Live regions&lt;/li&gt;
&lt;li&gt;Forced DOM updates&lt;/li&gt;
&lt;li&gt;Proper &lt;code&gt;role&lt;/code&gt; and &lt;code&gt;aria-*&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;Real testing with assistive tools&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Keyboard Accessibility&lt;/h2&gt;
&lt;p&gt;Keyboard navigation is a core WCAG requirement.&lt;/p&gt;
&lt;p&gt;Essential keys:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tab / Shift+Tab&lt;/strong&gt; — move focus&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enter / Space&lt;/strong&gt; — activate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arrow keys&lt;/strong&gt; — navigate inside composite widgets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escape&lt;/strong&gt; — close dialogs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Home / End&lt;/strong&gt; — jump to start/end&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tabindex=&quot;0&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tabindex=&quot;-1&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-activedescendant&lt;/code&gt; for virtual focus&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Testing Accessibility&lt;/h2&gt;
&lt;h3&gt;Browser Tools&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Axe DevTools&lt;/li&gt;
&lt;li&gt;Lighthouse&lt;/li&gt;
&lt;li&gt;WAVE&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Screen Readers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;NVDA&lt;/li&gt;
&lt;li&gt;JAWS&lt;/li&gt;
&lt;li&gt;VoiceOver&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Linters&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eslint-plugin-jsx-a11y&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CI&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Axe CI&lt;/li&gt;
&lt;li&gt;Pa11y CI&lt;/li&gt;
&lt;li&gt;Lighthouse CI&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example: Accessible Accordion Component&lt;/h2&gt;
&lt;p&gt;Here is &lt;strong&gt;a completely different example&lt;/strong&gt;, not related to the previous code and without any internal comments directed at you. It demonstrates a well‑structured, accessible accordion widget using WAI‑ARIA best practices.&lt;/p&gt;
&lt;h3&gt;HTML Structure&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;accordion&quot;&amp;gt;
  &amp;lt;h2 id=&quot;faq-title&quot;&amp;gt;FAQ&amp;lt;/h2&amp;gt;

  &amp;lt;div class=&quot;accordion__item&quot;&amp;gt;
    &amp;lt;button
      class=&quot;accordion__trigger&quot;
      aria-expanded=&quot;false&quot;
      aria-controls=&quot;sect-1&quot;
      id=&quot;accordion-btn-1&quot;
    &amp;gt;
      What is accessibility?
    &amp;lt;/button&amp;gt;
    &amp;lt;div
      class=&quot;accordion__panel&quot;
      id=&quot;sect-1&quot;
      role=&quot;region&quot;
      aria-labelledby=&quot;accordion-btn-1&quot;
      hidden
    &amp;gt;
      &amp;lt;p&amp;gt;
        Accessibility ensures that interfaces can be used by people with
        disabilities or assistive technologies.
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&quot;accordion__item&quot;&amp;gt;
    &amp;lt;button
      class=&quot;accordion__trigger&quot;
      aria-expanded=&quot;false&quot;
      aria-controls=&quot;sect-2&quot;
      id=&quot;accordion-btn-2&quot;
    &amp;gt;
      What is WAI‑ARIA?
    &amp;lt;/button&amp;gt;
    &amp;lt;div
      class=&quot;accordion__panel&quot;
      id=&quot;sect-2&quot;
      role=&quot;region&quot;
      aria-labelledby=&quot;accordion-btn-2&quot;
      hidden
    &amp;gt;
      &amp;lt;p&amp;gt;
        WAI‑ARIA provides roles and attributes that describe UI behavior to
        assistive technologies.
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;JavaScript Logic (Brand‑New Example)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class AriaAccordion {
  constructor(containerEl) {
    this.containerEl = containerEl;
    this.triggers = Array.from(
      containerEl.querySelectorAll(&quot;.accordion__trigger&quot;)
    );

    this.triggers.forEach(btn =&amp;gt; {
      btn.addEventListener(&quot;click&quot;, () =&amp;gt; this.toggle(btn));
      btn.addEventListener(&quot;keydown&quot;, e =&amp;gt; this.onKey(e, btn));
    });
  }

  toggle(buttonEl) {
    const isOpen = buttonEl.getAttribute(&quot;aria-expanded&quot;) === &quot;true&quot;;
    const newState = !isOpen;

    buttonEl.setAttribute(&quot;aria-expanded&quot;, newState);

    const panelId = buttonEl.getAttribute(&quot;aria-controls&quot;);
    const panel = document.getElementById(panelId);
    panel.hidden = !newState;
  }

  onKey(event, buttonEl) {
    const { code } = event;
    const index = this.triggers.indexOf(buttonEl);

    if (code === &quot;ArrowDown&quot;) {
      event.preventDefault();
      const next = this.triggers[index + 1] || this.triggers[0];
      next.focus();
    }

    if (code === &quot;ArrowUp&quot;) {
      event.preventDefault();
      const prev =
        this.triggers[index - 1] || this.triggers[this.triggers.length - 1];
      prev.focus();
    }
  }
}

document
  .querySelectorAll(&quot;.accordion&quot;)
  .forEach(acc =&amp;gt; new AriaAccordion(acc));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;ARIA is powerful, but with that power comes responsibility. Developers should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use semantic HTML whenever possible,&lt;/li&gt;
&lt;li&gt;apply ARIA roles and states only when appropriate,&lt;/li&gt;
&lt;li&gt;provide keyboard interaction patterns,&lt;/li&gt;
&lt;li&gt;test with real assistive technologies,&lt;/li&gt;
&lt;li&gt;and follow W3C ARIA Authoring Practices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By embracing these principles, you ensure your interface works smoothly for every user—regardless of device, ability, or assistive technology.&lt;/p&gt;
&lt;h2&gt;Related articles&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/howto/custom-html-tag/&quot;&gt;Mastering Custom Elements in HTML5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/html-output-tag/&quot;&gt;The Overlooked Power of the HTML &lt;code&gt;&amp;lt;output&amp;gt;&lt;/code&gt; Element&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/howto/wcag-compliance/&quot;&gt;Checking Web Pages for WCAG Compliance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Identify If a Value Belongs to a Class in JavaScript</title><link>https://jsdev.space/snippets/check-instance-js/</link><guid isPermaLink="true">https://jsdev.space/snippets/check-instance-js/</guid><description>Learn how to correctly check if a value is an instance of a class in JavaScript using prototype inspection, Symbol.hasInstance, and primitive mapping.</description><pubDate>Mon, 17 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Instance checking in JavaScript appears simple at first—just use &lt;code&gt;instanceof&lt;/code&gt;.&lt;br /&gt;
But real-world scenarios (and tasks like &lt;em&gt;LeetCode 2618&lt;/em&gt;) require correctly handling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;primitive values (&lt;code&gt;42&lt;/code&gt;, &lt;code&gt;&apos;hi&apos;&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;5n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;wrapper constructors (&lt;code&gt;Number&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;invalid constructor inputs&lt;/li&gt;
&lt;li&gt;functions as instances of &lt;code&gt;Function&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;prototype chain traversal&lt;/li&gt;
&lt;li&gt;custom instance logic via &lt;code&gt;Symbol.hasInstance&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This snippet provides three reliable solutions, rewritten and improved for clarity, correctness, and edge-case safety.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Solution 1 — Recommended&lt;/h2&gt;
&lt;h3&gt;Using &lt;code&gt;instanceof&lt;/code&gt; + Primitive Mapping&lt;/h3&gt;
&lt;p&gt;Works for objects and functions, with explicit special rules for built-in primitive wrappers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function isInstance(candidate, ctor) {
  if (ctor == null || typeof ctor !== &quot;function&quot;) return false;
  if (candidate == null) return false;

  if (candidate instanceof ctor) return true;

  const type = typeof candidate;
  if (ctor === Number) return type === &quot;number&quot;;
  if (ctor === String) return type === &quot;string&quot;;
  if (ctor === Boolean) return type === &quot;boolean&quot;;
  if (ctor === BigInt) return type === &quot;bigint&quot;;
  if (ctor === Symbol) return type === &quot;symbol&quot;;

  return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;h3&gt;Manual Prototype Chain Traversal&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function isInstanceByPrototype(value, ctor) {
  if (ctor == null || typeof ctor !== &quot;function&quot;) return false;
  if (value == null) return false;

  const type = typeof value;

  if (ctor === Number) return type === &quot;number&quot;;
  if (ctor === String) return type === &quot;string&quot;;
  if (ctor === Boolean) return type === &quot;boolean&quot;;
  if (ctor === BigInt) return type === &quot;bigint&quot;;
  if (ctor === Symbol) return type === &quot;symbol&quot;;

  if (type !== &quot;object&quot; &amp;amp;&amp;amp; type !== &quot;function&quot;) return false;

  let proto = Object.getPrototypeOf(value);
  const target = ctor.prototype;

  while (proto) {
    if (proto === target) return true;
    proto = Object.getPrototypeOf(proto);
  }

  return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Solution 3&lt;/h2&gt;
&lt;h3&gt;Using &lt;code&gt;Symbol.hasInstance&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function isInstanceBySymbol(value, ctor) {
  if (ctor == null || typeof ctor !== &quot;function&quot;) return false;
  if (value == null) return false;

  const hook = ctor[Symbol.hasInstance];
  if (typeof hook === &quot;function&quot; &amp;amp;&amp;amp; hook.call(ctor, value)) {
    return true;
  }

  const type = typeof value;
  if (ctor === Number) return type === &quot;number&quot;;
  if (ctor === String) return type === &quot;string&quot;;
  if (ctor === Boolean) return type === &quot;boolean&quot;;
  if (ctor === BigInt) return type === &quot;bigint&quot;;
  if (ctor === Symbol) return type === &quot;symbol&quot;;

  return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Examples&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Creature {}
class Feline extends Creature {}
function Demo() {}

console.log(isInstance(new Date(), Date));        
console.log(isInstance(new Feline(), Creature));  
console.log(isInstance(123, Number));             
console.log(isInstance(&quot;ok&quot;, String));            
console.log(isInstance(true, Boolean));           
console.log(isInstance(7n, BigInt));              
console.log(isInstance(Symbol(&quot;x&quot;), Symbol));     

console.log(isInstance(123, Object));             
console.log(isInstance(null, Object));            
console.log(isInstance(undefined, Number));       
console.log(isInstance(Creature, Creature));      
console.log(isInstance(Demo, Function));          
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Related content&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/snippets/comprehensive-websocket-client/&quot;&gt;WebSocket Client JavaScript Implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/snippets/deepfreeze-js/&quot;&gt;Deeply Freeze a Nested Object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/snippets/epsg4326-epsg3857/&quot;&gt;Convert coordinates from EPSG-4326 to EPSG-3857&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>