<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: javascript</title>
    <description>The latest articles tagged 'javascript' on DEV Community.</description>
    <link>https://dev.to/t/javascript</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/javascript"/>
    <language>en</language>
    <item>
      <title>How GSTIN Checksum Validation Works (and Why It Isn't Enough)</title>
      <dc:creator>Tarun Vaghasia</dc:creator>
      <pubDate>Sat, 08 Aug 2026 07:34:14 +0000</pubDate>
      <link>https://dev.to/tarun_vaghasia_a387e1ac9b/how-gstin-checksum-validation-works-and-why-it-isnt-enough-3l8e</link>
      <guid>https://dev.to/tarun_vaghasia_a387e1ac9b/how-gstin-checksum-validation-works-and-why-it-isnt-enough-3l8e</guid>
      <description>&lt;p&gt;Every Indian GST invoice carries a 15-character GSTIN. If you've ever had to validate one in code, you've probably written a regex like this and called it done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;/^&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That regex checks the &lt;em&gt;shape&lt;/em&gt; of a GSTIN. It says nothing about whether the number is real. A typo that happens to land on a syntactically valid string will sail straight through it — and if that typo ends up on an invoice, it's the kind of thing that gets flagged months later during a GST audit.&lt;/p&gt;

&lt;p&gt;There's a cheap next step before you go anywhere near a network call: the GSTIN has a built-in checksum character, and you can verify it in a few lines with no API and no dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the checksum works
&lt;/h2&gt;

&lt;p&gt;A GSTIN is 15 characters: &lt;code&gt;33AAACC1206D1ZN&lt;/code&gt;. The first 14 encode the state code, the business's PAN, and an entity number. The 15th is a checksum digit computed from the other 14, using a 36-character alphabet (&lt;code&gt;0-9&lt;/code&gt; then &lt;code&gt;A-Z&lt;/code&gt;) and alternating weights of 1 and 2 — the same family of algorithm as ISO 7064 MOD 37-36.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ALPHABET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gstinChecksumIsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9A-Z&lt;/span&gt;&lt;span class="se"&gt;]{15}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codePoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ALPHABET&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;digit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;codePoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digit&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digit&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checksumIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ALPHABET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;checksumIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;gstinChecksumIsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;33AAACC1206D1ZN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;gstinChecksumIsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;33AAACC1206D1ZM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false — last char changed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hand-verified this against a real, currently-registered GSTIN (Central Warehousing Corporation, &lt;code&gt;33AAACC1206D1ZN&lt;/code&gt;) before trusting it enough to write this post — the algorithm above reproduces the &lt;code&gt;N&lt;/code&gt; checksum character exactly.&lt;/p&gt;

&lt;p&gt;Python, same logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ALPHABET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gstin_checksum_is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^[0-9A-Z]{15}$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;code_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ALPHABET&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;digit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;code_point&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;digit&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;digit&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;
        &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;checksum_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ALPHABET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;checksum_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;gstin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop this in ahead of any network call. It rejects a huge class of typos and malformed input for free, in constant time, offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a checksum can't tell you
&lt;/h2&gt;

&lt;p&gt;A checksum only proves the string is &lt;em&gt;internally consistent&lt;/em&gt; — that it could plausibly be a GSTIN. It doesn't tell you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether that GSTIN is actually registered&lt;/li&gt;
&lt;li&gt;whether the registration is still active, or was cancelled&lt;/li&gt;
&lt;li&gt;what legal name it's registered under, so you can catch a mismatch against the invoice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can construct a string that's checksum-valid and still refers to nobody, or to a business whose registration was cancelled last year. For that you need to ask the actual GST network, which means an API call.&lt;/p&gt;

&lt;p&gt;That's the part I build: &lt;a href="https://www.gstinapi.in/" rel="noopener noreferrer"&gt;gstinapi.in&lt;/a&gt; is a REST API that takes a GSTIN and returns the legal name, registration status, taxpayer type and registered address, live from India's official GSP network — no scraping, no cached copy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GstinApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gstinapi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// npm install gstinapi&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GstinApi&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GSTIN_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;33AAACC1206D1ZN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;legal_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// CENTRAL WAREHOUSING CORPORATION&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// Active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;gstinapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GstinApi&lt;/span&gt;  &lt;span class="c1"&gt;# pip install gstinapi
&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GstinApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GSTIN_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;33AAACC1206D1ZN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;legal_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# CENTRAL WAREHOUSING CORPORATION
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;      &lt;span class="c1"&gt;# Active
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every account gets 100 free lookups a month, no credit card, and it renews every month rather than being a one-time trial credit — plenty to validate the idea before you need volume.&lt;/p&gt;

&lt;p&gt;Source for the client libraries: &lt;a href="https://github.com/CsoftTarun/gstinapi-node" rel="noopener noreferrer"&gt;gstinapi-node&lt;/a&gt; and &lt;a href="https://github.com/CsoftTarun/gstinapi-python" rel="noopener noreferrer"&gt;gstinapi-python&lt;/a&gt;. Full API reference is at &lt;a href="https://www.gstinapi.in/docs" rel="noopener noreferrer"&gt;gstinapi.in/docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you build GSTIN validation into something, I'd like to hear where the checksum check alone was enough and where you actually needed the live lookup — drop it in the comments.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Evolution of Scientific Computing: Why Developers Still Need Dedicated Calculators</title>
      <dc:creator>Study HUB</dc:creator>
      <pubDate>Sat, 08 Aug 2026 07:10:09 +0000</pubDate>
      <link>https://dev.to/studyhubnetin/the-evolution-of-scientific-computing-why-developers-still-need-dedicated-calculators-3pao</link>
      <guid>https://dev.to/studyhubnetin/the-evolution-of-scientific-computing-why-developers-still-need-dedicated-calculators-3pao</guid>
      <description>&lt;p&gt;As developers, we are surrounded by immense computational power. When we need to solve a math problem, our first instinct is often to fire up a Python REPL, open the Node.js terminal, or simply type Math.sin(90) into the browser's developer console. We build complex algorithms, design neural networks, and render 3D environments. Yet, despite having entire programming ecosystems at our fingertips, there is still a massive, undeniable need for a dedicated, accessible scientific calculator.&lt;br&gt;
Why? Because context switching is the enemy of productivity, and raw programming languages don't always offer the immediate, visual feedback required for complex mathematical modeling.&lt;br&gt;
The Hidden Complexity of Developer Math&lt;br&gt;
Whether you are programming a physics engine for a new web game, calculating CSS transform matrices, or designing algorithmic trading bots, advanced mathematics is baked into the fabric of software engineering.&lt;br&gt;
However, doing complex trigonometry or logarithm calculations in a raw terminal can be frustrating. You have to remember language-specific syntax, manage nested parentheses meticulously to respect the Order of Operations (PEMDAS/BODMAS), and deal with the notorious IEEE 754 double-precision floating-point format. We’ve all seen the classic 0.1 + 0.2 = 0.30000000000000004 quirk. When you are trying to verify the orbital mechanics of a simulation or the attenuation of a signal in a telecommunications app, you need a tool that handles the math cleanly and visually.&lt;br&gt;
Enter the Dedicated Online Scientific Calculator&lt;br&gt;
This is where having a reliable, browser-based mathematical tool becomes a game-changer. Rather than writing a script to double-check your formula, you can use a purpose-built application.&lt;br&gt;
Recently, I came across the highly efficient Scientific Calculator by StudyHUB. Engineered specifically as a free open educational resource, this tool bridges the gap between raw computing and user-friendly mathematics.&lt;br&gt;
Unlike basic desktop calculators, StudyHUB’s Scientific Calculator provides a fully responsive web interface packed with advanced computational capabilities. It allows you to step away from your IDE for a moment and visually construct your equations.&lt;br&gt;
Features That Make a Difference&lt;br&gt;
When evaluating an online scientific calculator for professional or academic use, there are a few non-negotiable features that StudyHUB nails perfectly:&lt;br&gt;
Radian and Degree Toggles: If you've ever spent hours debugging a graphics rendering issue only to realize your Math library uses radians while your brain was calculating in degrees, you know this pain. A one-click toggle eliminates this cognitive load.&lt;br&gt;
Advanced Trigonometry &amp;amp; Logarithms: Full support for hyperbolic functions (sinh, cosh, tanh), natural logarithms (ln), and inverse functions natively mapped to an intuitive UI.&lt;br&gt;
Memory Registers: The inclusion of MC, MR, M+, and M- functions allows developers to store intermediate variables without needing to write them down or hold them in a clipboard.&lt;br&gt;
Platform Agnostic: Because it is built as a lightweight web tool, it works flawlessly whether you are on a multi-monitor desktop setup or quickly checking a formula on your smartphone during a meeting.&lt;br&gt;
A Tool for the Modern STEM Professional&lt;br&gt;
While we will always rely on our code to execute math at scale, verifying the logic behind that code requires a different set of tools. Dedicated scientific calculators act as the sandbox where we can test our fundamental mathematical assumptions before translating them into syntax.&lt;br&gt;
If you are a computer science student working through discrete math, or a seasoned developer fine-tuning a complex CSS animation curve, bookmarking a reliable tool is a must. Check out the StudyHUB Scientific Calculator and keep it in your developer toolkit. It’s fast, accurate, and completely free—exactly what the modern web demands.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
    <item>
      <title>You built your modal with a `&lt;div&gt;` and a focus trap library. The native `&lt;dialog&gt;` does all of that.</title>
      <dc:creator>Parsa Jiravand</dc:creator>
      <pubDate>Sat, 08 Aug 2026 07:00:10 +0000</pubDate>
      <link>https://dev.to/parsajiravand/you-built-your-modal-with-a-and-a-focus-trap-library-the-native-does-all-of-15lg</link>
      <guid>https://dev.to/parsajiravand/you-built-your-modal-with-a-and-a-focus-trap-library-the-native-does-all-of-15lg</guid>
      <description>&lt;p&gt;Building a modal from scratch means writing the same boilerplate every time: a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with &lt;code&gt;role="dialog"&lt;/code&gt;, an &lt;code&gt;aria-modal&lt;/code&gt; attribute, a &lt;code&gt;tabindex="-1"&lt;/code&gt; to steal focus, a &lt;code&gt;keydown&lt;/code&gt; listener to catch Escape, a focus-trap library to keep Tab cycling inside the dialog, and a &lt;code&gt;click&lt;/code&gt; listener on the backdrop overlay you rendered yourself. It works, but you're doing the browser's job.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element exists to take that job back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The baseline API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"confirm-dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Delete item?&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This cannot be undone.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cancel-btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"confirm-btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"open-btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirm-dialog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cancelBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancel-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirm-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;openBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;cancelBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;confirmBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;deleteItem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The browser handles focus trapping (Tab stays inside), Escape-to-close, and ARIA role. No library. No &lt;code&gt;keydown&lt;/code&gt; listener. No backdrop div.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;showModal()&lt;/code&gt; vs &lt;code&gt;show()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The element has two open methods and they are meaningfully different.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dialog.show()&lt;/code&gt; opens the dialog as a &lt;strong&gt;non-modal&lt;/strong&gt;: it's visible, but the rest of the page is still interactive. Useful for inline panels, drawers, or toasts — not for blocking confirmation flows.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dialog.showModal()&lt;/code&gt; opens it as a &lt;strong&gt;modal&lt;/strong&gt;: the browser places the dialog in the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Top_layer" rel="noopener noreferrer"&gt;top layer&lt;/a&gt; — above all other content, including elements with high &lt;code&gt;z-index&lt;/code&gt; — and blocks interaction with everything beneath. Focus is trapped inside. Escape closes it. This is what you actually want for a modal dialog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// non-modal — rest of page still interactive&lt;/span&gt;
&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// modal — top layer, focus trapped, Escape works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styling the backdrop
&lt;/h2&gt;

&lt;p&gt;When opened with &lt;code&gt;showModal()&lt;/code&gt;, the browser renders a backdrop behind the dialog and above the rest of the page. You style it with the &lt;code&gt;::backdrop&lt;/code&gt; pseudo-element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;dialog&lt;/span&gt;&lt;span class="nd"&gt;::backdrop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;backdrop-filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more &lt;code&gt;position: fixed; inset: 0; background: rgba(0,0,0,0.5)&lt;/code&gt; divs. The browser-rendered backdrop is always in the right place, covers the right things, and is animated by the dialog's own open/close transitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Animating open and close
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element pairs naturally with &lt;code&gt;@starting-style&lt;/code&gt; for entry animations. For the exit, you need a small JS helper because the dialog's &lt;code&gt;close()&lt;/code&gt; method removes the &lt;code&gt;open&lt;/code&gt; attribute before a CSS exit transition can play:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;dialog&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;200ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;200ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="m"&gt;200ms&lt;/span&gt; &lt;span class="n"&gt;allow-discrete&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;dialog&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@starting-style&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;dialog&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;allow-discrete&lt;/code&gt; on the &lt;code&gt;display&lt;/code&gt; transition (Chrome 117+), the browser holds the dialog in the layout for the duration of the exit transition before hiding it. For older targets, a small &lt;code&gt;setTimeout&lt;/code&gt; before &lt;code&gt;dialog.close()&lt;/code&gt; is the reliable fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling backdrop clicks to close
&lt;/h2&gt;

&lt;p&gt;The backdrop is not a separate element you can listen to directly. The reliable pattern uses the dialog's own &lt;code&gt;click&lt;/code&gt; event and checks whether the click landed inside the dialog's bounding box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clickedOutside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clickedOutside&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason this works: when you click the backdrop, the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element itself receives the event — the click target is the dialog, not an element inside it. The bounding box check distinguishes backdrop clicks from content clicks cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Return value from &lt;code&gt;close()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;dialog.close()&lt;/code&gt; accepts an optional string argument that becomes the dialog's &lt;code&gt;returnValue&lt;/code&gt;. This lets you communicate &lt;em&gt;why&lt;/em&gt; the dialog closed without external state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;confirmBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;cancelBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returnValue&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;deleteItem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;close&lt;/code&gt; event fires whenever the dialog closes — via &lt;code&gt;close()&lt;/code&gt;, Escape, or a &lt;code&gt;&amp;lt;form method="dialog"&amp;gt;&lt;/code&gt; submission. Combined with &lt;code&gt;returnValue&lt;/code&gt;, it gives you a clean interface for confirmation flows without threading state through callbacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;&amp;lt;form method="dialog"&amp;gt;&lt;/code&gt; shortcut
&lt;/h2&gt;

&lt;p&gt;If you put a &lt;code&gt;&amp;lt;form method="dialog"&amp;gt;&lt;/code&gt; inside a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, any submit button closes the dialog automatically and sets &lt;code&gt;returnValue&lt;/code&gt; to the button's &lt;code&gt;value&lt;/code&gt; attribute — no JavaScript needed for the close logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"confirm-dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Delete item?&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"cancelled"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"confirmed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returnValue&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;deleteItem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The form doesn't submit to a server — &lt;code&gt;method="dialog"&lt;/code&gt; is a special value that routes the submission back to the dialog element itself. Useful for simple flows; for complex forms with validation, you'll still handle submission explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; is &lt;strong&gt;Baseline 2022&lt;/strong&gt;: Chrome 98, Firefox 98, Safari 15.4. Global support is above 95%. You do not need a polyfill for production today. The only missing piece in some older browsers is &lt;code&gt;::backdrop&lt;/code&gt; styling — the functional modal behavior (focus trap, Escape, top layer) is universally supported.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://daily-post-dev.netlify.app/posts/2026-08-08-dialog-element-native-modal/playground/" rel="noopener noreferrer"&gt;▶️ Open the interactive playground →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Runs right in your browser — poke at it and watch the concept react live.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Test yourself
&lt;/h2&gt;

&lt;p&gt;Think it clicked? &lt;strong&gt;&lt;a href="https://daily-post-dev.netlify.app/quiz/take.html?post=2026-08-08-dialog-element-native-modal" rel="noopener noreferrer"&gt;Take the 8-question quiz →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Search your codebase for &lt;code&gt;role="dialog"&lt;/code&gt; and the focus-trap imports that live near it. Each one is a candidate for replacement: &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; with &lt;code&gt;showModal()&lt;/code&gt; handles focus trapping, Escape, backdrop, and ARIA semantics — a library bought you those because the platform didn't provide them. It does now. Start with the simplest confirmation dialog in your UI, replace it with &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; and &lt;code&gt;showModal()&lt;/code&gt;, and you'll notice what disappears: the &lt;code&gt;keydown&lt;/code&gt; listener, the backdrop div, the z-index war, and the focus-management ceremony. What remains is the logic that was actually yours to write.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! Let's stay connected:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;GitHub&lt;/strong&gt; — follow me and star the projects: &lt;a href="https://github.com/parsajiravand" rel="noopener noreferrer"&gt;github.com/parsajiravand&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Discord&lt;/strong&gt; — join the frontend best-practices community: &lt;a href="https://discord.gg/d9KRhuAwQ" rel="noopener noreferrer"&gt;discord.gg/d9KRhuAwQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📸 &lt;strong&gt;Instagram&lt;/strong&gt; — frontend best practices, daily: &lt;a href="https://www.instagram.com/bestpractice___/" rel="noopener noreferrer"&gt;@bestpractice___&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💼 &lt;strong&gt;LinkedIn&lt;/strong&gt; — &lt;a href="https://www.linkedin.com/in/parsa-jiravand/" rel="noopener noreferrer"&gt;linkedin.com/in/parsa-jiravand&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✉️ &lt;strong&gt;Email&lt;/strong&gt; (work &amp;amp; contract inquiries): &lt;a href="mailto:bestpractice2026@gmail.com"&gt;bestpractice2026@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Stop importing `uuid`. `crypto.randomUUID()` has been native since 2021.</title>
      <dc:creator>Parsa Jiravand</dc:creator>
      <pubDate>Sat, 08 Aug 2026 07:00:09 +0000</pubDate>
      <link>https://dev.to/parsajiravand/stop-importing-uuid-cryptorandomuuid-has-been-native-since-2021-ehm</link>
      <guid>https://dev.to/parsajiravand/stop-importing-uuid-cryptorandomuuid-has-been-native-since-2021-ehm</guid>
      <description>&lt;p&gt;The &lt;code&gt;uuid&lt;/code&gt; npm package is one of the most downloaded libraries in the JavaScript ecosystem. The vast majority of those installs exist for one thing: &lt;code&gt;uuid/v4&lt;/code&gt;, which generates a random unique identifier in the standard &lt;code&gt;xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx&lt;/code&gt; format. The browser — and Node.js — have been doing this natively since 2021. No package needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The native API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// "110e8400-e29b-41d4-a716-446655440000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire API surface. One call, no constructor, no configuration. It returns a v4 UUID string — the same format every UUID library produces, the same format your database expects.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;crypto.randomUUID()&lt;/code&gt; lives on the Web Crypto API's &lt;code&gt;crypto&lt;/code&gt; object, which is globally available in browsers, Node.js, Deno, Bun, and Cloudflare Workers. No import required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's safe to trust
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;uuid/v4&lt;/code&gt; package generates UUIDs using &lt;code&gt;crypto.getRandomValues()&lt;/code&gt; internally — the same source &lt;code&gt;crypto.randomUUID()&lt;/code&gt; uses. Both draw from the operating system's CSPRNG (cryptographically secure pseudorandom number generator). The native method adds nothing risky; it's the same primitive, one level closer to the metal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What uuid/v4 does internally (simplified):&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRandomValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// ... set version and variant bits, format as string&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// What crypto.randomUUID() does:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// same operation, no library&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The security profile is identical. The library adds no cryptographic benefit over the native call.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;crypto.randomUUID()&lt;/code&gt; is available in any &lt;strong&gt;secure context&lt;/strong&gt; — HTTPS pages, &lt;code&gt;localhost&lt;/code&gt;, Service Workers, Web Workers, and Node.js 14.17+.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a Service Worker&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// In a Web Worker&lt;/span&gt;
&lt;span class="c1"&gt;// Inside worker.js:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "secure context" requirement is the only constraint. If your page is served over HTTPS (or you're on &lt;code&gt;localhost&lt;/code&gt;), &lt;code&gt;crypto&lt;/code&gt; is available everywhere the JavaScript runtime runs in that page — main thread, workers, service worker included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing the import
&lt;/h2&gt;

&lt;p&gt;A direct swap, wherever you currently use &lt;code&gt;uuid&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output format is identical. Whether you're generating IDs in a React component, a route handler, a database model, or a logging utility — it's a one-line change per call site. Once every call is replaced, you can remove the dependency from &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one case where you still need a library
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;crypto.randomUUID()&lt;/code&gt; generates &lt;strong&gt;v4 UUIDs only&lt;/strong&gt; — random, no namespace. If you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;v5 UUIDs&lt;/strong&gt; (namespace + name, SHA-1 hash based): still use &lt;code&gt;uuid/v5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v3 UUIDs&lt;/strong&gt; (namespace + name, MD5 hash based): still use &lt;code&gt;uuid/v3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parsing or inspecting&lt;/strong&gt; UUID bytes, version bits, or variant: still use the library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're generating a unique ID for a database row, a request trace ID, a session token, or a UI list item key — v4 is the right choice. That's exactly what &lt;code&gt;crypto.randomUUID()&lt;/code&gt; gives you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;crypto.randomUUID()&lt;/code&gt; is &lt;strong&gt;Baseline 2022&lt;/strong&gt;: Chrome 92 (July 2021), Firefox 95 (November 2021), Safari 15.4 (March 2022), Node.js 14.17 (May 2021). It has been in every supported browser and server runtime for years. There's nothing to polyfill for any currently-maintained target.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://daily-post-dev.netlify.app/posts/2026-08-08-crypto-randomuuid-native-uuid/playground/" rel="noopener noreferrer"&gt;▶️ Open the interactive playground →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Runs right in your browser — poke at it and watch the concept react live.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Test yourself
&lt;/h2&gt;

&lt;p&gt;Think it clicked? &lt;strong&gt;&lt;a href="https://daily-post-dev.netlify.app/quiz/take.html?post=2026-08-08-crypto-randomuuid-native-uuid" rel="noopener noreferrer"&gt;Take the 9-question quiz →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Open your &lt;code&gt;package.json&lt;/code&gt;. If &lt;code&gt;uuid&lt;/code&gt; is listed and your codebase only uses &lt;code&gt;v4&lt;/code&gt;, remove it. Search for &lt;code&gt;uuid/v4&lt;/code&gt;, &lt;code&gt;uuidv4()&lt;/code&gt;, or &lt;code&gt;import { v4 }&lt;/code&gt; and replace each call with &lt;code&gt;crypto.randomUUID()&lt;/code&gt;. You get the same string format, the same randomness quality, and zero added bundle weight. For v4 UUIDs — the most common case by a wide margin — the native method is strictly better than the library it replaces.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! Let's stay connected:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;GitHub&lt;/strong&gt; — follow me and star the projects: &lt;a href="https://github.com/parsajiravand" rel="noopener noreferrer"&gt;github.com/parsajiravand&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Discord&lt;/strong&gt; — join the frontend best-practices community: &lt;a href="https://discord.gg/d9KRhuAwQ" rel="noopener noreferrer"&gt;discord.gg/d9KRhuAwQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📸 &lt;strong&gt;Instagram&lt;/strong&gt; — frontend best practices, daily: &lt;a href="https://www.instagram.com/bestpractice___/" rel="noopener noreferrer"&gt;@bestpractice___&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💼 &lt;strong&gt;LinkedIn&lt;/strong&gt; — &lt;a href="https://www.linkedin.com/in/parsa-jiravand/" rel="noopener noreferrer"&gt;linkedin.com/in/parsa-jiravand&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✉️ &lt;strong&gt;Email&lt;/strong&gt; (work &amp;amp; contract inquiries): &lt;a href="mailto:bestpractice2026@gmail.com"&gt;bestpractice2026@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>performance</category>
    </item>
    <item>
      <title>Building an accessible Nigerian restaurant landing page with vanilla JavaScript and hand-drawn SVG</title>
      <dc:creator>Benjamin Fadina</dc:creator>
      <pubDate>Sat, 08 Aug 2026 06:59:16 +0000</pubDate>
      <link>https://dev.to/benjaminsqlserver/building-an-accessible-nigerian-restaurant-landing-page-with-vanilla-javascript-and-hand-drawn-svg-32pb</link>
      <guid>https://dev.to/benjaminsqlserver/building-an-accessible-nigerian-restaurant-landing-page-with-vanilla-javascript-and-hand-drawn-svg-32pb</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Iya Bashirat Restaurant&lt;/strong&gt; is a landing page for a fictional Nigerian restaurant&lt;br&gt;
at 56 Ajayi Crowther Street, Victoria Island, Lagos.&lt;/p&gt;

&lt;p&gt;Iya Bashirat has been cooking on the same street since 1998. Victoria Island grew&lt;br&gt;
up around her: glass towers, banks, traffic that does not move. She is still&lt;br&gt;
pounding yam by hand at 6am because a machine makes it gummy. That is the comfort&lt;br&gt;
food I wanted. Not a trend, just a woman who never changed the recipe.&lt;/p&gt;

&lt;p&gt;The menu is seventeen real Nigerian dishes: party jollof, abula, ofada and&lt;br&gt;
ayamase, egusi with pounded yam, suya, asun, catfish pepper soup, moi moi, puff&lt;br&gt;
puff, chapman, zobo, kunu aya. Each one carries its Yoruba name, a heat rating,&lt;br&gt;
allergens and dietary tags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built with vanilla HTML, CSS and ES modules. No framework, no build step, and&lt;br&gt;
not a single network request.&lt;/strong&gt; No CDN, no web font, no analytics, no map embed,&lt;br&gt;
no images. Every dish, the portrait of Iya Bashirat at the fire, and the&lt;br&gt;
neighbourhood map are hand-drawn inline SVG.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A menu that actually works as a tool.&lt;/strong&gt; Filter by course, filter by dietary
need, search across names/descriptions/allergens, sort by price or by heat.
Every dish opens a detail dialog with serving size, allergens and heat level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An order tray that hands off to WhatsApp&lt;/strong&gt;, because that is how a Lagos
restaurant genuinely takes orders. Quantities, pickup or delivery, a note for
the kitchen, and it survives a page reload. No payment, no backend, no
pretending.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opening hours computed in &lt;code&gt;Africa/Lagos&lt;/code&gt;&lt;/strong&gt;, not in your timezone. The badge
says "Open now", "Closing soon" or "Closed", the hours table marks today, and
the reservation form generates its time slots from that same data, so you
physically cannot request a table for a night the kitchen is shut.&lt;/li&gt;
&lt;li&gt;Reservation form with progressive validation, a review carousel, scroll-spy
navigation, light/dark theming, and toasts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Benjamin-Fadina/embed/gbgEGyy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/benjaminsqlserver/food-front-end" rel="noopener noreferrer"&gt;https://github.com/benjaminsqlserver/food-front-end&lt;/a&gt; (MIT licensed)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;Order on WhatsApp&lt;/strong&gt; buttons open a new tab, which CodePen's sandboxed&lt;br&gt;
preview sometimes blocks. Open the Pen in its own tab to try the full&lt;br&gt;
hand-off. The order message is composed either way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Pen is not a flattened copy. CodePen gives you one JS pane, and this is 17&lt;br&gt;
modules wired by 40 relative imports. Instead of stripping every&lt;br&gt;
&lt;code&gt;import&lt;/code&gt;/&lt;code&gt;export&lt;/code&gt; and hand-ordering the dependency graph, the Pen loads the real&lt;br&gt;
files from the repository over jsDelivr, pinned to a git tag. &lt;code&gt;main.js&lt;/code&gt; resolves&lt;br&gt;
its own relative imports against the CDN exactly as it does when served locally.&lt;br&gt;
Nothing is bundled, minified or rewritten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;I went in treating &lt;strong&gt;accessibility as a build constraint rather than an audit at&lt;br&gt;
the end&lt;/strong&gt;, and it changed most of my architecture decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; instead of a hand-rolled modal.&lt;/strong&gt; All three modals use&lt;br&gt;
&lt;code&gt;showModal()&lt;/code&gt;, which gives focus trapping, Escape-to-close, background inertness&lt;br&gt;
and the top layer for free. Every one of those I would have got subtly wrong by&lt;br&gt;
hand. The only thing it does not give you is an exit animation, since &lt;code&gt;close()&lt;/code&gt;&lt;br&gt;
pulls the element out of the top layer instantly. So I drive an &lt;code&gt;.is-open&lt;/code&gt; class&lt;br&gt;
and defer the real &lt;code&gt;close()&lt;/code&gt; until the transition ends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The filter chips are real radios and checkboxes.&lt;/strong&gt; I nearly built them as&lt;br&gt;
&lt;code&gt;aria-pressed&lt;/code&gt; buttons. Using real form controls meant grouping, arrow-key&lt;br&gt;
navigation and state announcement all came from the platform. But it exposed a&lt;br&gt;
trap I had not thought about: the common &lt;code&gt;.visually-hidden&lt;/code&gt; utility has a&lt;br&gt;
&lt;code&gt;:not(:focus)&lt;/code&gt; guard so skip links can reveal themselves, which means a hidden&lt;br&gt;
radio &lt;em&gt;pops back into the layout&lt;/em&gt; the moment someone tabs to it. Hidden form&lt;br&gt;
controls need unconditional clipping, with the focus ring drawn on the label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contrast checking caught the thing I was proudest of.&lt;/strong&gt; I wrote a script to&lt;br&gt;
check every foreground/background pair in both themes. Everything passed except&lt;br&gt;
my WhatsApp buttons: white on WhatsApp's own &lt;code&gt;#25D366&lt;/code&gt; is &lt;strong&gt;2.2:1&lt;/strong&gt;, nowhere near&lt;br&gt;
AA. Their brand colour is inaccessible as a button fill. I darkened it to&lt;br&gt;
&lt;code&gt;#12803f&lt;/code&gt;, which still reads as WhatsApp, hits 5.0:1 with white, and still clears&lt;br&gt;
3:1 as a shape against both page backgrounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autoplay you can actually defend.&lt;/strong&gt; The review carousel never starts under&lt;br&gt;
&lt;code&gt;prefers-reduced-motion&lt;/code&gt;, pauses on hover &lt;em&gt;and&lt;/em&gt; on focus, stops in a background&lt;br&gt;
tab, and always shows a visible pause control. The scrolling ribbon under the&lt;br&gt;
hero stops dead and reflows to a static wrapped list when reduced motion is on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anchor links move focus, not just the viewport.&lt;/strong&gt; Clicking "Menu" scrolls you&lt;br&gt;
there and hands focus to the section, so the next Tab continues from where your&lt;br&gt;
eye already is. It is three lines of code and almost nobody does it.&lt;/p&gt;

&lt;p&gt;Since I had no browser automation available, I verified by writing tests: a&lt;br&gt;
selector audit that proves every &lt;code&gt;#id&lt;/code&gt;, &lt;code&gt;data-*&lt;/code&gt; hook, &lt;code&gt;aria-describedby&lt;/code&gt; target&lt;br&gt;
and &lt;code&gt;&amp;lt;use href&amp;gt;&lt;/code&gt; resolves; a &lt;strong&gt;62-assertion jsdom suite&lt;/strong&gt; that boots the real&lt;br&gt;
modules and drives filtering, sorting, the tray totals, reservation validation and&lt;br&gt;
the carousel; &lt;strong&gt;19 assertions pinning the Lagos timezone logic&lt;/strong&gt; to fixed UTC&lt;br&gt;
instants; a structural accessibility audit; and the contrast check. All green.&lt;/p&gt;

&lt;p&gt;The part I am happiest with is the artwork. Drawing seventeen dishes as SVG by&lt;br&gt;
hand took longer than anything else, but it means zero image requests, zero layout&lt;br&gt;
shift, perfect sharpness at any size, and the whole page still weighs less than&lt;br&gt;
one photograph of a plate of jollof.&lt;/p&gt;

&lt;p&gt;Iya Bashirat is fictional. The address, phone number, handles and reviews are&lt;br&gt;
invented for this challenge, and nothing on the page contacts a real business.&lt;/p&gt;

&lt;p&gt;Code is MIT licensed.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Making Online Registration Easier for Small Business Users</title>
      <dc:creator>Rahul Deka</dc:creator>
      <pubDate>Sat, 08 Aug 2026 06:44:54 +0000</pubDate>
      <link>https://dev.to/rahul_deka1/making-online-registration-easier-for-small-business-users-2okh</link>
      <guid>https://dev.to/rahul_deka1/making-online-registration-easier-for-small-business-users-2okh</guid>
      <description>&lt;p&gt;Explain that many small-business owners struggle with online registration forms because of unfamiliar terms, long forms, unclear classifications, and fear of entering something incorrectly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use simple language&lt;br&gt;
Discuss why government or business terminology should be explained in words ordinary users understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make activity selection easier&lt;br&gt;
Explain the difficulty users have when choosing NIC codes and why search, examples, descriptions, and clear categories improve the experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Show important information before the form&lt;br&gt;
Mention that users should know which details they need before starting instead of discovering requirements halfway through.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Design for mobile users&lt;br&gt;
Explain the importance of readable text, clear buttons, simple navigation, and forms that work properly on smaller screens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make private and official services clear&lt;br&gt;
Explain why websites should clearly distinguish private assistance from official government services. This improves user trust and prevents confusion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Give users helpful resources&lt;br&gt;
Here you can naturally mention your real project in your own words:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While working with &lt;a href="https://instantudyam.com/" rel="noopener noreferrer"&gt;Instant Udyam&lt;/a&gt;, I have seen how useful clear NIC-code information and step-by-step guidance can be for small-business users.&lt;/p&gt;

&lt;p&gt;Then, if you are publishing under an Instant Udyam DEV organization and the link genuinely supports the discussion, you can reference:&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>ux</category>
      <category>product</category>
    </item>
    <item>
      <title>Node.js Has a Test Runner Now: Field Notes on Dropping Jest for Scripts and Libraries</title>
      <dc:creator>Ahmed Mahmoud</dc:creator>
      <pubDate>Sat, 08 Aug 2026 06:32:54 +0000</pubDate>
      <link>https://dev.to/ahmed_mahmoud360/nodejs-has-a-test-runner-now-field-notes-on-dropping-jest-for-scripts-and-libraries-i89</link>
      <guid>https://dev.to/ahmed_mahmoud360/nodejs-has-a-test-runner-now-field-notes-on-dropping-jest-for-scripts-and-libraries-i89</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Headline:&lt;/strong&gt; Node.js ships a built-in test runner — &lt;code&gt;node --test&lt;/code&gt; executes &lt;code&gt;node:test&lt;/code&gt; files with no Jest, no Vitest, and no config file — and paired with type stripping it runs &lt;code&gt;.ts&lt;/code&gt; tests with zero build. I moved my scripts and small libraries onto it: &lt;code&gt;node:test&lt;/code&gt; for structure, &lt;code&gt;node:assert/strict&lt;/code&gt; for assertions, the built-in &lt;code&gt;mock&lt;/code&gt; for spies and fake timers, and &lt;code&gt;--experimental-test-coverage&lt;/code&gt; for coverage. I kept Vitest for anything that touches the DOM or renders React.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Node.js test runner lives in the built-in &lt;code&gt;node:test&lt;/code&gt; module and runs with &lt;code&gt;node --test&lt;/code&gt;; it needs no dependency, no config file, and has been stable since Node.js 20.&lt;/li&gt;
&lt;li&gt;Assertions come from the built-in &lt;code&gt;node:assert&lt;/code&gt; module — import &lt;code&gt;node:assert/strict&lt;/code&gt; so &lt;code&gt;assert.equal&lt;/code&gt; uses strict (&lt;code&gt;===&lt;/code&gt;) comparison.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;mock&lt;/code&gt; object from &lt;code&gt;node:test&lt;/code&gt; gives you &lt;code&gt;mock.fn()&lt;/code&gt;, &lt;code&gt;mock.method()&lt;/code&gt;, and &lt;code&gt;mock.timers&lt;/code&gt; without a separate mocking library; module mocking via &lt;code&gt;mock.module()&lt;/code&gt; is still experimental.&lt;/li&gt;
&lt;li&gt;Combined with type stripping, &lt;code&gt;node --test "src/**/*.test.ts"&lt;/code&gt; runs TypeScript tests with no build step on Node.js 22.18+ and Node.js 24.&lt;/li&gt;
&lt;li&gt;Coverage is available behind &lt;code&gt;--experimental-test-coverage&lt;/code&gt;, and output format is chosen with &lt;code&gt;--test-reporter&lt;/code&gt; (&lt;code&gt;spec&lt;/code&gt;, &lt;code&gt;tap&lt;/code&gt;, &lt;code&gt;dot&lt;/code&gt;, &lt;code&gt;junit&lt;/code&gt;, &lt;code&gt;lcov&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Can Node.js run tests without Jest or Vitest?
&lt;/h2&gt;

&lt;p&gt;Yes — Node.js has a built-in test runner, and it needs zero dependencies. The runner is the &lt;code&gt;node:test&lt;/code&gt; core module, invoked with &lt;code&gt;node --test&lt;/code&gt;; it landed experimentally in Node.js 18 and became stable in Node.js 20. It auto-discovers test files, runs each in its own child process for isolation, and reports in a TAP-based format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// math.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./math.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add sums two numbers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;node --test&lt;/code&gt;. No &lt;code&gt;jest.config.js&lt;/code&gt;, no transform, no &lt;code&gt;ts-jest&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I structure tests with node:test?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;node:test&lt;/code&gt; exports &lt;code&gt;test&lt;/code&gt;, plus &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;it&lt;/code&gt; and the hooks &lt;code&gt;before&lt;/code&gt;, &lt;code&gt;after&lt;/code&gt;, &lt;code&gt;beforeEach&lt;/code&gt;, &lt;code&gt;afterEach&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeEach&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;starts empty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sums item prices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Focus one test with &lt;code&gt;{ only: true }&lt;/code&gt; and &lt;code&gt;--test-only&lt;/code&gt;, or filter with &lt;code&gt;--test-name-pattern&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I mock functions and timers?
&lt;/h2&gt;

&lt;p&gt;Mocking is built in through the &lt;code&gt;mock&lt;/code&gt; object — no &lt;code&gt;jest.fn&lt;/code&gt; to install.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notifies the customer once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="nx"&gt;notify&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callCount&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mock.method(obj, 'name')&lt;/code&gt; spies on a real method, and &lt;code&gt;mock.timers.enable({ apis: ['setTimeout'] })&lt;/code&gt; plus &lt;code&gt;mock.timers.tick(1000)&lt;/code&gt; gives deterministic fake timers. Full module mocking via &lt;code&gt;mock.module()&lt;/code&gt; is still experimental, so I favor dependency injection instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can I run TypeScript tests without a build step?
&lt;/h2&gt;

&lt;p&gt;Yes, on Node.js 22.18+ and Node.js 24, because Node.js strips type annotations at load time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--test&lt;/span&gt; &lt;span class="s2"&gt;"src/**/*.test.ts"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Relative imports need the explicit &lt;code&gt;.ts&lt;/code&gt; extension, and Node.js never type-checks — run &lt;code&gt;tsc --noEmit&lt;/code&gt; as a separate CI gate. This removed &lt;code&gt;ts-jest&lt;/code&gt; and the Vitest transform from every script and library repo I own.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I get coverage and CI reports?
&lt;/h2&gt;

&lt;p&gt;Watch mode is &lt;code&gt;node --test --watch&lt;/code&gt;. Coverage is a flag: &lt;code&gt;node --test --experimental-test-coverage&lt;/code&gt; prints a per-file table. Reporters are chosen with &lt;code&gt;--test-reporter&lt;/code&gt;, and you can emit several at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--test&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--test-reporter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;spec &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--test-reporter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;junit &lt;span class="nt"&gt;--test-reporter-destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;junit.xml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--experimental-test-coverage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thresholds like &lt;code&gt;--test-coverage-lines=80&lt;/code&gt; (Node.js 22+) fail the run below a percentage — enough to gate a PR without a coverage service.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should I keep Vitest or Jest?
&lt;/h2&gt;

&lt;p&gt;Keep them whenever a test needs a browser-like environment or a build transform the runtime lacks.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Node.js test runner&lt;/th&gt;
&lt;th&gt;Vitest / Jest&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies&lt;/td&gt;
&lt;td&gt;Zero (built in)&lt;/td&gt;
&lt;td&gt;A framework + transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOM / React&lt;/td&gt;
&lt;td&gt;No jsdom, no JSX&lt;/td&gt;
&lt;td&gt;jsdom/happy-dom + JSX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Native via type stripping&lt;/td&gt;
&lt;td&gt;esbuild/SWC transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshots&lt;/td&gt;
&lt;td&gt;Basic, recent&lt;/td&gt;
&lt;td&gt;Mature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Scripts, libraries, backend&lt;/td&gt;
&lt;td&gt;Frontend, components, big suites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Backend and library code gets the built-in runner; anything that renders a component stays on Vitest.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is the Node.js test runner production-ready?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A:&lt;/strong&gt; Yes — the runner and &lt;code&gt;node:test&lt;/code&gt; API are stable since Node.js 20. Only module mocking and coverage remain behind experimental flags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Do I need a config file?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A:&lt;/strong&gt; No. &lt;code&gt;node --test&lt;/code&gt; auto-discovers test files and takes options as CLI flags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Which assertion library does it use?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A:&lt;/strong&gt; The built-in &lt;code&gt;node:assert&lt;/code&gt;; import &lt;code&gt;node:assert/strict&lt;/code&gt; for strict equality. Third-party assertion libraries still work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can it test React components?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A:&lt;/strong&gt; Not well — no DOM, no JSX transform. Use Vitest/Jest with jsdom for components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How does it mock ES modules?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A:&lt;/strong&gt; Via &lt;code&gt;mock.module()&lt;/code&gt;, still experimental. I prefer dependency injection for stable code.&lt;/p&gt;




&lt;p&gt;Originally published on &lt;a href="https://www.devya.dev/blogs/nodejs-native-test-runner-field-notes" rel="noopener noreferrer"&gt;devya.dev&lt;/a&gt;. Also on &lt;a href="https://www.eng-ahmed.com/blog/nodejs-native-test-runner-field-notes" rel="noopener noreferrer"&gt;eng-ahmed.com&lt;/a&gt;. Built by &lt;a href="https://www.devya.dev" rel="noopener noreferrer"&gt;Devya Solutions&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>node</category>
      <category>testing</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Possessed by Production: The Ghost Execution That Ran Code Nobody Ever Wrote</title>
      <dc:creator>Bhavnish</dc:creator>
      <pubDate>Sat, 08 Aug 2026 06:29:04 +0000</pubDate>
      <link>https://dev.to/bhavnish_e35294bf0fd0b2df/possessed-by-production-the-ghost-execution-that-ran-code-nobody-ever-wrote-50mf</link>
      <guid>https://dev.to/bhavnish_e35294bf0fd0b2df/possessed-by-production-the-ghost-execution-that-ran-code-nobody-ever-wrote-50mf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I deleted a single &lt;code&gt;console.log&lt;/code&gt; statement from our payment worker, merged the PR, and went to grab tea. Ten minutes later, PagerDuty was screaming.&lt;/p&gt;

&lt;p&gt;Memory usage on our background pods spiked to 99.8%. Outgoing webhooks failed with orphan closure errors, and Postgres rows started getting overwritten with payloads from different users.&lt;/p&gt;

&lt;p&gt;I panicked and reverted the commit. I put the &lt;code&gt;console.log&lt;/code&gt; back.&lt;/p&gt;

&lt;p&gt;And just like magic... the errors stopped. Memory dropped back to 180MB. Everything went green.&lt;/p&gt;

&lt;p&gt;Reverting a one-line PR and having production fix itself is terrifying. It means your architecture isn't actually stable—it's riding on accidental side-effects.&lt;/p&gt;




&lt;h2&gt;
  
  
  14 Hours in DevTools: What Happened?
&lt;/h2&gt;

&lt;p&gt;If adding &lt;code&gt;console.log&lt;/code&gt; fixes your backend, you are relying on luck and OS buffer timing.&lt;/p&gt;

&lt;p&gt;I spent 14 hours taking heap snapshots (&lt;code&gt;node --inspect&lt;/code&gt;) and diffing memory dumps across V8 garbage collection cycles. Here is what happened under the hood:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Timing Trap:&lt;/strong&gt; In Node.js, printing to &lt;code&gt;stdout&lt;/code&gt; isn't always instant and non-blocking. That tiny log call introduces microsecond I/O pauses that inadvertently acted as an accidental sync guard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Floating Promise:&lt;/strong&gt; We had a background logging task running inside an un-awaited Promise chain inside our request handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V8 Scope Reclamation:&lt;/strong&gt; When &lt;code&gt;console.log(user.id)&lt;/code&gt; was present, V8 retained an explicit reference to &lt;code&gt;user&lt;/code&gt; in the parent scope. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I removed the log, V8 JIT optimizations kicked in. During aggressive Garbage Collection, V8 marked the parent context as dead before the background callback finished writing to Redis. The callback resolved into an invalidated scope context.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Broken Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/checkout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;telemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;queued&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AsyncLocalStorage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:async_hooks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/checkout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;trx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;telemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checkout pipeline failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What This Incident Taught Me
&lt;/h2&gt;

&lt;p&gt;Accidental synchronization is real: If removing a log breaks your service, you have a race condition.&lt;/p&gt;

&lt;p&gt;Respect V8 internals: JIT compilers are aggressive. Don't leave floating background callbacks hoping scope stays alive.&lt;/p&gt;

&lt;p&gt;Always await your promises: Unhandled floating promises will eventually bite you under high concurrency.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Next.js Image Optimization: next/image Deep Dive for Production Apps</title>
      <dc:creator>Aon infotech</dc:creator>
      <pubDate>Sat, 08 Aug 2026 06:28:08 +0000</pubDate>
      <link>https://dev.to/aon_infotech_3a1b6ff525fc/nextjs-image-optimization-nextimage-deep-dive-for-production-apps-2kn</link>
      <guid>https://dev.to/aon_infotech_3a1b6ff525fc/nextjs-image-optimization-nextimage-deep-dive-for-production-apps-2kn</guid>
      <description>&lt;p&gt;Images consistently account for 50–70% of total page weight in most web applications. Next.js ships a built-in &lt;code&gt;next/image&lt;/code&gt; component that handles the heavy lifting automatically — but using it correctly means understanding what it actually does, what configuration matters in production, and where the defaults fall short.&lt;/p&gt;

&lt;h2&gt;
  
  
  What next/image Actually Does
&lt;/h2&gt;

&lt;p&gt;When you use &lt;code&gt;next/image&lt;/code&gt;, requests go through Next.js's image optimization API at &lt;code&gt;/_next/image&lt;/code&gt;. On each request it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Converts to WebP or AVIF (significantly smaller than JPEG/PNG)&lt;/li&gt;
&lt;li&gt;Resizes to the requested display dimensions&lt;/li&gt;
&lt;li&gt;Lazy loads by default&lt;/li&gt;
&lt;li&gt;Reserves layout space to prevent Cumulative Layout Shift&lt;/li&gt;
&lt;li&gt;Caches the optimized output server-side
Self-hosted deployments need &lt;code&gt;sharp&lt;/code&gt; for server-side optimization:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;sharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Vercel, optimization runs at the edge automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Implementation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;heroImage&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/public/hero.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Local image — dimensions extracted at build time&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Hero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;heroImage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Descriptive alt text here"&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Remote image — must specify dimensions&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;imageUrl&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Product"&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100vw, 400px"&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The &lt;code&gt;sizes&lt;/code&gt; Prop — Most Commonly Misused
&lt;/h2&gt;

&lt;p&gt;Without &lt;code&gt;sizes&lt;/code&gt;, Next.js generates a single large image. With it, the browser receives multiple sizes and selects the correct one for the current viewport.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong — browser downloads large image regardless of display size&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Correct — sizes match actual CSS rendering&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 800px"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sizes&lt;/code&gt; string describes your CSS layout. If the image is full-width on mobile, half-width on tablet, and 800px fixed on desktop, write exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;fill&lt;/code&gt; for Flexible Containers
&lt;/h2&gt;

&lt;p&gt;When you don't know dimensions ahead of time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative h-64 w-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Cover"&lt;/span&gt;
    &lt;span class="na"&gt;fill&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"object-cover"&lt;/span&gt;
    &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100vw, 50vw"&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parent needs &lt;code&gt;position: relative&lt;/code&gt; and defined dimensions. &lt;code&gt;fill&lt;/code&gt; makes the image absolutely positioned to match the parent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Priority Loading for LCP
&lt;/h2&gt;

&lt;p&gt;The Largest Contentful Paint image should never be lazy loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Any image visible on page load — hero, banner, above-fold card&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;heroSrc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Hero"&lt;/span&gt;
  &lt;span class="na"&gt;fill&lt;/span&gt;
  &lt;span class="na"&gt;priority&lt;/span&gt;
  &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;priority&lt;/code&gt; adds a preload link and disables lazy loading. Don't add it to off-screen images — that wastes bandwidth on content users may not scroll to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Domains Configuration
&lt;/h2&gt;

&lt;p&gt;Next.js blocks external images by default. Configure &lt;code&gt;remotePatterns&lt;/code&gt; in &lt;code&gt;next.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;remotePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/uploads/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**.cloudinary.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// wildcard subdomain&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;remotePatterns&lt;/code&gt; over the deprecated &lt;code&gt;domains&lt;/code&gt; array — it gives you path-level control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality, Format, and Cache Configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;// default 75 — increase for sharper images&lt;/span&gt;
    &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/avif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// AVIF first, WebP fallback&lt;/span&gt;
    &lt;span class="na"&gt;deviceSizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;828&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;minimumCacheTTL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// cache for 24h instead of default 60s&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AVIF compresses better than WebP but encodes slower. Browser negotiation handles the format selection — keep both in the array.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blur Placeholder
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Local image — blur data generated at build time automatically&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;productImage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Product"&lt;/span&gt;
  &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"blur"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Remote image — provide base64 blurDataURL&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;remoteUrl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Product"&lt;/span&gt;
  &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"blur"&lt;/span&gt;
  &lt;span class="na"&gt;blurDataURL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"data:image/jpeg;base64,/9j/4AAQ..."&lt;/span&gt;
  &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The blur shows immediately while the full image loads. Perceived performance improves significantly even when actual load time stays the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sharp is not optional for self-hosted apps.&lt;/strong&gt; Without it, Next.js falls back to a slower path or disables optimization entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory watch on high-traffic sites.&lt;/strong&gt; Each unique URL/size combination gets cached. Applications serving user-uploaded images with unique filenames can exhaust cache memory. Set a reasonable &lt;code&gt;minimumCacheTTL&lt;/code&gt; and monitor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SVGs bypass the optimizer.&lt;/strong&gt; SVG files are served as-is — the optimizer is designed for raster images. Handle SVGs with SVGR or as static assets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build-time vs request-time dimensions.&lt;/strong&gt; Local imports have their dimensions extracted at build time. Remote URLs need &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; specified in the component or &lt;code&gt;fill&lt;/code&gt; layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring the Impact
&lt;/h2&gt;

&lt;p&gt;Track these before and after switching to &lt;code&gt;next/image&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LCP score in Core Web Vitals (primary image performance metric)&lt;/li&gt;
&lt;li&gt;Total image transfer size in DevTools Network tab filtered by Img&lt;/li&gt;
&lt;li&gt;CLS score — proper dimension reservation eliminates most image-related layout shift
For most apps serving photographic content, correctly configured &lt;code&gt;next/image&lt;/code&gt; reduces image payload by 50–70% and brings CLS to near zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For AI-generated imagery like the content at &lt;a href="https://www.pixova.io/blog/free-ai-sketch-generator" rel="noopener noreferrer"&gt;Pixova's free sketch generator&lt;/a&gt;, the WebP conversion from PNG source files typically produces 40–60% size reduction — meaningful at scale when thousands of users are loading AI-generated images daily.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>performance</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I Tried Building JavaScript Games Without a Game Engine. Here's What I Learned</title>
      <dc:creator>Ravindra Reddy Chitla</dc:creator>
      <pubDate>Sat, 08 Aug 2026 05:50:43 +0000</pubDate>
      <link>https://dev.to/ravindrachitla/i-tried-building-javascript-games-without-a-game-engine-heres-what-i-learned-18b9</link>
      <guid>https://dev.to/ravindrachitla/i-tried-building-javascript-games-without-a-game-engine-heres-what-i-learned-18b9</guid>
      <description>&lt;p&gt;I am a digital marketer, not a professional developer or game developer. Most of my career has been focused on SEO, growth marketing, paid acquisition, content, and digital strategy. When I started building GamesMom, however, I found myself learning much more about web development than I expected.&lt;/p&gt;

&lt;p&gt;GamesMom is a &lt;a href="https://gamesmom.com/" rel="noopener noreferrer"&gt;collection of free educational games and learning activities for kids&lt;/a&gt; that run directly in the browser. The site includes math games, word games, typing games, memory games, puzzle games, classroom games, quizzes, and other interactive activities. The idea was simple: make games that children can open and play without downloading an application or creating an account.&lt;/p&gt;

&lt;p&gt;I initially assumed that building browser games would require a dedicated game engine or a large JavaScript framework. After experimenting with different approaches, I found that many of the games I wanted to create could be built with ordinary HTML, CSS, and JavaScript.&lt;/p&gt;

&lt;p&gt;That was probably the most useful lesson I learned from the project. You don't always need a complicated technology stack to create an interactive web experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I Started With the Simplest Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you're not a professional developer, it is tempting to look for the most sophisticated solution available. I did this too.&lt;/p&gt;

&lt;p&gt;I spent time looking at frameworks, game engines, libraries, and different ways of structuring interactive applications. Eventually I started asking a much simpler question: what does this particular game actually need?&lt;/p&gt;

&lt;p&gt;A basic educational game might need to display a question, accept an answer, update a score, show feedback, and move to the next question. Another might need a timer, a few buttons, and some randomization.&lt;/p&gt;

&lt;p&gt;Those requirements don't automatically justify a game engine.&lt;/p&gt;

&lt;p&gt;For simple browser games, the browser already provides a lot of what you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML, CSS and JavaScript Can Go a Long Way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The basic combination is surprisingly capable.&lt;/p&gt;

&lt;p&gt;HTML provides the structure of the page. CSS controls the visual presentation and responsive layout. JavaScript handles the interaction and game logic.&lt;/p&gt;

&lt;p&gt;That combination was enough for many of the interactive experiences I wanted to create.&lt;/p&gt;

&lt;p&gt;This also made the project easier for me to understand. Instead of learning a large development environment before I could build anything, I could take one problem at a time and learn the JavaScript or browser feature required to solve it.&lt;/p&gt;

&lt;p&gt;That approach was much less intimidating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding Events Changed Everything&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most important concepts I encountered was the browser event system.&lt;/p&gt;

&lt;p&gt;A player clicks a button. They press a keyboard key. They select an answer. They interact with something on a touchscreen.&lt;/p&gt;

&lt;p&gt;JavaScript can listen for these actions and respond.&lt;/p&gt;

&lt;p&gt;For example, a simple interaction can look like this:&lt;/p&gt;

&lt;p&gt;button.addEventListener("click", () =&amp;gt; {&lt;br&gt;
  checkAnswer();&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;There isn't much code here, but understanding what is happening is important. The browser waits for an event, JavaScript receives it, and the application responds.&lt;/p&gt;

&lt;p&gt;Once I understood that pattern, many game interactions became easier to think about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timers Are Simple Until You Start Testing Them&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Timers are another feature that looks trivial until you actually build something around them.&lt;/p&gt;

&lt;p&gt;A timed math game, typing game, countdown activity, or classroom timer needs to know when the timer starts, when it stops, and what happens when the time reaches zero.&lt;/p&gt;

&lt;p&gt;A simple implementation can use JavaScript's built in timing functions.&lt;/p&gt;

&lt;p&gt;let timerId;&lt;/p&gt;

&lt;p&gt;function startTimer() {&lt;br&gt;
  clearInterval(timerId);&lt;/p&gt;

&lt;p&gt;timerId = setInterval(() =&amp;gt; {&lt;br&gt;
    updateTimer();&lt;br&gt;
  }, 1000);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function stopTimer() {&lt;br&gt;
  clearInterval(timerId);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;The important lesson for me wasn't memorizing setInterval().&lt;/p&gt;

&lt;p&gt;It was understanding that every temporary process needs a clear lifecycle. If something starts, you need to know when it should stop.&lt;/p&gt;

&lt;p&gt;That principle applies to much more than games.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Randomness Makes Games Replayable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot of educational games become more useful when the experience changes each time someone plays.&lt;/p&gt;

&lt;p&gt;A math game can generate different questions. A word game can select different words. A memory activity can shuffle cards. A quiz can select questions from a larger pool.&lt;/p&gt;

&lt;p&gt;JavaScript provides simple tools for random selection.&lt;/p&gt;

&lt;p&gt;const index = Math.floor(Math.random() * items.length);&lt;br&gt;
const item = items[index];&lt;/p&gt;

&lt;p&gt;The interesting part comes after that.&lt;/p&gt;

&lt;p&gt;You need to think about whether something can repeat too often, whether the difficulty remains appropriate, and whether the player is actually getting a different experience.&lt;/p&gt;

&lt;p&gt;For example, one of the quizzes currently available on GamesMom selects questions from a larger pool and shuffles both the questions and answer positions between rounds.&lt;/p&gt;

&lt;p&gt;That turns a simple randomization function into an actual product decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Every Game Needs Canvas&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before working on the project, I assumed that interactive games would naturally require Canvas.&lt;/p&gt;

&lt;p&gt;That isn't always the case.&lt;/p&gt;

&lt;p&gt;If a game is primarily made up of questions, text, buttons, cards, scores, and simple interactions, ordinary HTML elements can work very well.&lt;/p&gt;

&lt;p&gt;JavaScript can update the page when something changes:&lt;/p&gt;

&lt;p&gt;scoreElement.textContent = score;&lt;br&gt;
questionElement.textContent = currentQuestion;&lt;/p&gt;

&lt;p&gt;For many educational games, this approach is perfectly reasonable.&lt;/p&gt;

&lt;p&gt;It also has a practical advantage. Normal HTML elements can work naturally with browser features such as text selection, zooming, keyboard interaction, and accessibility tools.&lt;/p&gt;

&lt;p&gt;For the types of games I was building, I didn't need to make everything a custom graphical surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile Testing Changed My Thinking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest differences between building a normal website and building browser games is interaction.&lt;/p&gt;

&lt;p&gt;A button that works well with a mouse may not work well on a phone.&lt;/p&gt;

&lt;p&gt;A small card can be easy to click with a cursor and frustrating to tap with a finger.&lt;/p&gt;

&lt;p&gt;As I tested games on different screen sizes, I became much more aware of touch targets, spacing, responsive layouts, font sizes, and the amount of information displayed on the screen.&lt;/p&gt;

&lt;p&gt;GamesMom is intended to work across phones, tablets, laptops, and classroom displays, so responsive design became part of the development process rather than something to consider at the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility Is Part of the User Experience&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also learned that building &lt;a href="https://gamesmom.com/games/" rel="noopener noreferrer"&gt;games for children&lt;/a&gt; changes the way you think about accessibility.&lt;/p&gt;

&lt;p&gt;An interactive element needs to be easy to identify and use. Text needs to remain readable. Controls should not depend entirely on a mouse. The interface needs to remain usable across different screen sizes.&lt;/p&gt;

&lt;p&gt;This doesn't require an advanced accessibility framework.&lt;/p&gt;

&lt;p&gt;It requires paying attention.&lt;/p&gt;

&lt;p&gt;Keyboard navigation, visible focus states, readable typography, appropriate contrast, sensible touch targets, and simple interactions can make a significant difference.&lt;/p&gt;

&lt;p&gt;The more I worked on the project, the more I realized that accessibility isn't something you bolt onto a finished website. It is part of the interface from the beginning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Becomes More Important as the Site Grows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single game can hide a lot of inefficiency.&lt;/p&gt;

&lt;p&gt;When you start building a larger collection of browser games, those decisions become more important.&lt;/p&gt;

&lt;p&gt;Images need to be appropriately sized. JavaScript should do useful work rather than unnecessary work. Pages shouldn't load resources that the visitor doesn't need. Animations shouldn't exist simply because they look impressive.&lt;/p&gt;

&lt;p&gt;This is one reason I liked keeping the individual games relatively focused.&lt;/p&gt;

&lt;p&gt;Someone playing a typing game shouldn't need to load the resources required by an unrelated memory game.&lt;/p&gt;

&lt;p&gt;Keeping experiences focused can improve both the development process and the experience for the person using the site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reusable Code Is Useful, But There Is a Limit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest lessons from building multiple games was learning where reuse makes sense.&lt;/p&gt;

&lt;p&gt;Some functionality naturally repeats. Scores, buttons, timers, result screens, and common interface elements can often follow the same patterns.&lt;/p&gt;

&lt;p&gt;But the actual experience of each game should still feel appropriate to what the player is doing.&lt;/p&gt;

&lt;p&gt;If you make everything too reusable, you can end up forcing completely different games into the same structure.&lt;/p&gt;

&lt;p&gt;The goal isn't to make every game identical.&lt;/p&gt;

&lt;p&gt;The goal is to avoid solving the same technical problem repeatedly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Building Games Taught Me About Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest lesson wasn't a particular JavaScript function or browser API.&lt;/p&gt;

&lt;p&gt;It was learning how to break unfamiliar technical problems into smaller problems.&lt;/p&gt;

&lt;p&gt;Coming from digital marketing, I didn't approach GamesMom with years of software engineering experience. I approached it by learning what I needed, testing different approaches, reading documentation, using development tools, and fixing problems as they appeared.&lt;/p&gt;

&lt;p&gt;That experience changed how I look at websites.&lt;/p&gt;

&lt;p&gt;As a marketer, it is easy to think about a website primarily in terms of traffic, rankings, conversions, content, and acquisition.&lt;/p&gt;

&lt;p&gt;Building the product myself made me think more about what happens underneath those metrics.&lt;/p&gt;

&lt;p&gt;Performance affects the user experience. Interface decisions affect engagement. Accessibility affects who can use the product. Architecture affects how easily a site can grow.&lt;/p&gt;

&lt;p&gt;Those things are connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Would Do Differently&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I started again, I would spend more time defining the common building blocks before creating a large number of games.&lt;/p&gt;

&lt;p&gt;I would also test on mobile devices earlier and establish accessibility and performance standards before the number of pages and games became large.&lt;/p&gt;

&lt;p&gt;Most importantly, I would avoid adding technology simply because it is popular.&lt;/p&gt;

&lt;p&gt;If a simple JavaScript solution works, there is no prize for making it complicated.&lt;/p&gt;

&lt;p&gt;If a project eventually reaches the point where a framework or game engine solves a real problem, then introduce it.&lt;/p&gt;

&lt;p&gt;The technology should respond to the requirements of the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You Don't Have to Be a Developer to Start Building&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wouldn't describe myself as a developer because that isn't my profession.&lt;/p&gt;

&lt;p&gt;But building GamesMom taught me that the barrier between marketing and development is much lower than I previously thought.&lt;/p&gt;

&lt;p&gt;You don't need to know everything before starting a technical project.&lt;/p&gt;

&lt;p&gt;You need to be willing to learn enough to solve the next problem.&lt;/p&gt;

&lt;p&gt;That might mean learning JavaScript today, responsive web design tomorrow, and performance optimization next week.&lt;/p&gt;

&lt;p&gt;The process is incremental.&lt;/p&gt;

&lt;p&gt;You don't have to become an expert in everything before you build your first useful thing.&lt;/p&gt;

&lt;p&gt;I started GamesMom because I wanted to create free educational games and &lt;a href="https://gamesmom.com/learn/" rel="noopener noreferrer"&gt;learning activities&lt;/a&gt; that children could use directly in a browser. Along the way, the project became an unexpected education in web development.&lt;/p&gt;

&lt;p&gt;I learned that HTML, CSS, and JavaScript can go surprisingly far when the problem is relatively simple. I learned that good mobile experiences require more than making a desktop layout responsive. I learned that accessibility and performance need to be considered early. Most importantly, I learned that adding more technology isn't automatically the answer to a technical problem.&lt;/p&gt;

&lt;p&gt;Sometimes the best solution is the simplest one you understand well enough to improve.&lt;/p&gt;

&lt;p&gt;And that may be the most useful thing I took away from building browser games as a marketer rather than a developer.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built an AI Customer Support Chatbot for Shopify That Resolved 80% of Tickets</title>
      <dc:creator>Khanzadi Wazir Ali</dc:creator>
      <pubDate>Sat, 08 Aug 2026 05:23:11 +0000</pubDate>
      <link>https://dev.to/khanzadigithubid/how-i-built-an-ai-customer-support-chatbot-for-shopify-that-resolved-80-of-tickets-4g9c</link>
      <guid>https://dev.to/khanzadigithubid/how-i-built-an-ai-customer-support-chatbot-for-shopify-that-resolved-80-of-tickets-4g9c</guid>
      <description>&lt;p&gt;My client was drowning in support tickets. Same questions, every single day.&lt;/p&gt;

&lt;p&gt;Their team was spending 20+ hours a week just answering repetitive queries. That's when they came to me — and I built them an AI chatbot that changed everything.&lt;/p&gt;

&lt;p&gt;The Problem&lt;br&gt;
The store was getting ~150 support messages per week. 80% of them were the same 10 questions. This was slow, expensive, and burning out the team.&lt;/p&gt;

&lt;p&gt;The Stack&lt;br&gt;
OpenAI API (GPT-4)&lt;br&gt;
Node.js + Express.js&lt;br&gt;
Shopify REST API&lt;br&gt;
n8n (workflow automation)&lt;br&gt;
The Results&lt;br&gt;
Metric  Before  After&lt;br&gt;
Tickets resolved without human  20% 80%+&lt;br&gt;
Avg response time   4 hours instant&lt;br&gt;
Monthly support cost    $1,200  ~$200&lt;br&gt;
What I Learned&lt;br&gt;
Context is everything. A chatbot that pulls live data is 10x more useful than one answering from static text.&lt;/p&gt;

&lt;p&gt;Always build an escape hatch. The escalation flow made the client trust the system.&lt;/p&gt;

&lt;p&gt;Prompt engineering matters more than model size.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>shopify</category>
      <category>javascript</category>
    </item>
    <item>
      <title>AI Coding Assistants vs Full Stack Developers: Where Each Works Best in 2026</title>
      <dc:creator>Kundan Parmar</dc:creator>
      <pubDate>Sat, 08 Aug 2026 04:53:45 +0000</pubDate>
      <link>https://dev.to/kundanparmarseo/ai-coding-assistants-vs-full-stack-developers-3bk8</link>
      <guid>https://dev.to/kundanparmarseo/ai-coding-assistants-vs-full-stack-developers-3bk8</guid>
      <description>&lt;p&gt;Let me tell you about a conversation I had with a startup founder last quarter.&lt;/p&gt;

&lt;p&gt;He'd spent six weeks trying to build his SaaS product using an &lt;a href="https://en.wikipedia.org/wiki/AI-assisted_software_development" rel="noopener noreferrer"&gt;AI coding assistant&lt;/a&gt;. No developers on payroll. Just him, a few smart prompts, and a lot of optimism. The prototype worked beautifully in his local environment. Then he tried to connect a payment gateway. Then add user roles. Then make the thing actually secure enough to show investors.&lt;/p&gt;

&lt;p&gt;Three weeks later, he called us. The codebase was a tangle of AI-generated functions that worked individually but contradicted each other at the seams. No developer on his team could untangle it because there was no team. Just layers of confident, wrong code.&lt;/p&gt;

&lt;p&gt;He's not alone. And I'm not sharing this to bash AI tools -- I use them every day and they genuinely save me hours. The point is that the "AI vs developers" conversation keeps getting framed as a competition when it's really a question of fit. Different problems. Different tools. Getting that wrong in either direction costs you time and money.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Full Stack Development Actually Covers
&lt;/h2&gt;

&lt;p&gt;People use "full stack" loosely, so let's be clear about what it means in practice.&lt;/p&gt;

&lt;p&gt;A full stack developer owns the entire product experience from the database schema through to what a user sees on screen. That means designing how data is stored, writing the logic that processes it, building the API that connects backend to frontend, and assembling the UI that users interact with. In 2026, most full stack developers working on serious products also handle deployment, cloud infrastructure, and increasingly, AI feature integration -- connecting LLM APIs, setting up vector search, wiring retrieval pipelines.&lt;/p&gt;

&lt;p&gt;What it doesn't mean: one developer doing the work of a ten-person team forever. Full stack means you can navigate the whole system without needing a translator between layers. It doesn't mean infinite bandwidth.&lt;/p&gt;

&lt;p&gt;The technologies vary. React and Next.js dominate on the frontend right now. Node.js, Python (FastAPI in particular has grown quickly), and Go handle the backend depending on the use case. PostgreSQL holds the top database spot by adoption. MongoDB remains the go-to for flexible document-heavy applications. TypeScript has stopped being optional -- if you're not using it in 2026 on a professional codebase, teams notice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Also Read: &lt;a href="https://ourcodeworld.com/articles/read/4248/why-full-stack-mern-developers-deliver-faster-lessons-from-our-own-mistakes" rel="noopener noreferrer"&gt;Why Full-Stack MERN Developers Deliver Faster&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Honest Picture on AI Coding Tools
&lt;/h2&gt;

&lt;p&gt;Here's what AI coding assistants are genuinely good at, from someone who watches development teams use them daily.&lt;/p&gt;

&lt;p&gt;Autocomplete that actually understands context. The days of tab-completion that guesses the variable name are over. Tools like GitHub Copilot and Cursor now follow the logic of what you're building and suggest the next logical block of code, not just the next word. For repetitive patterns -- API routes, form validation, test boilerplate -- this is legitimately fast.&lt;/p&gt;

&lt;p&gt;Explaining code you didn't write. This one gets underestimated. When a developer joins a project mid-stream and needs to understand what a 400-line function does, asking an AI to explain it is faster and often clearer than reading documentation that may not exist. Same with legacy code that predates your team.&lt;/p&gt;

&lt;p&gt;First-draft unit tests. Writing tests for a well-defined function with clear inputs and outputs is exactly the kind of bounded, predictable task AI handles well. Not integration tests. Not tests that need to understand business rules and edge cases. But the routine test coverage that developers avoid because it's tedious -- AI can do a lot of that.&lt;/p&gt;

&lt;p&gt;Where it breaks down is less obvious but more expensive when it happens. AI tools have no memory between sessions. They have no awareness of decisions your team made three months ago and why. They don't know that you migrated away from a particular library because of a security issue, or that a specific pattern is banned in your codebase because it caused a production incident. Every prompt starts fresh. For greenfield projects with simple architecture, that's manageable. For real products with real history, it's a constant friction point.&lt;/p&gt;

&lt;p&gt;The deeper issue is quality verification. GitHub Copilot generates roughly 46% of code for active users, but only about 30% of its suggestions get accepted without modification. That's not a failure -- that's how it's supposed to work. The problem is when teams stop doing that verification step because the code looks right and shipping feels urgent. An independent analysis published in late 2025 found significantly more issues in AI-coauthored pull requests than in human-written code. The code gets generated faster. The problems just move downstream.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Also Read: &lt;a href="https://blog.stackademic.com/what-is-a-full-stack-developer-how-to-hire-one-who-delivers-8c9c845607c8" rel="noopener noreferrer"&gt;What Is a Full Stack Developer? How to Hire One Who Delivers&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Is Full Stack Development Still Worth Investing In?
&lt;/h2&gt;

&lt;p&gt;I get this question from engineering managers and founders more often now than I did two years ago.&lt;/p&gt;

&lt;p&gt;The answer is yes, and the data actually supports it pretty clearly. The World Economic Forum's 2025 Future of Jobs report listed software developers among the top growing roles in raw headcount, not just percentage growth. The Bureau of Labor Statistics has 17% growth projected for software developers through 2033. &lt;a href="https://www.gartner.com/en/newsroom/press-releases/2024-10-03-gartner-says-generative-ai-will-require-80-percent-of-engineering-workforce-to-upskill-through-2027" rel="noopener noreferrer"&gt;Gartner's position&lt;/a&gt; is that generative AI creates new engineering roles, not fewer of them -- their prediction that 80% of engineers will need to upskill through 2027 is often quoted as a warning sign, but their actual conclusion is that AI expands what engineers do, not that it contracts who does it.&lt;/p&gt;

&lt;p&gt;What has changed is the entry-level market. Routine tasks that used to fill a junior developer's first year -- generating boilerplate, writing straightforward CRUD functions, producing templated reports -- have been significantly absorbed by AI tools. Many engineering teams have raised their expectations for new hires accordingly. "Junior" in 2026 means something different than it did in 2022.&lt;/p&gt;

&lt;p&gt;But mid-level and senior full stack developers who can architect systems, make real tradeoff decisions, and own code in production? Demand is solid. If anything, the premium on genuine seniority has gone up because AI tools can now do a convincing impression of a junior developer. Teams need people who can tell the difference between code that works and code that will hold up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Also Read: &lt;a href="https://www.hiddenbrains.com/blog/hire-react-native-developers-real-app-cost-breakdown.html" rel="noopener noreferrer"&gt;Hire React Native Developers: Real App Cost Breakdown&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where AI Coding Assistants Actually Win
&lt;/h2&gt;

&lt;p&gt;Specific scenarios where the tool beats the developer, or at least matches them at a fraction of the cost:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building a prototype quickly.&lt;/strong&gt; If you need to demonstrate a concept to investors or test a product hypothesis, AI-assisted scaffolding gets you to something clickable in days rather than weeks. The code doesn't need to be production-quality. It needs to show the idea. AI tools are genuinely good at that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accelerating experienced developers.&lt;/strong&gt; The productivity gains from AI tools are most significant when the person using them already knows what good code looks like. A senior developer using Copilot can move at a pace that would've required a small team a few years ago. That's not replacing developers -- it's compressing timelines for developers who already have the judgment to use AI outputs selectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation and code explanation.&lt;/strong&gt; This is quietly one of the most valuable use cases and it rarely gets mentioned. Writing documentation is something most developers would rather avoid. AI tools write a coherent first draft in seconds. Explaining what a function does, generating README files, writing API documentation -- all of this is low-risk, high-value territory for AI assistance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repetitive test generation.&lt;/strong&gt; When a developer needs 40 unit tests for 40 similar functions, that's a job for AI. It's also exactly the kind of work that burns out good engineers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Also Read: &lt;a href="https://www.hiddenbrains.com/blog/why-us-saas-companies-choose-mern-stack-faster-development.html" rel="noopener noreferrer"&gt;Why US SaaS Companies Still Choose MERN Stack for Faster Product Development&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where Full Stack Developers Are Irreplaceable
&lt;/h2&gt;

&lt;p&gt;Architecture is the clearest answer. Deciding how a system is structured -- what services exist, how they communicate, where data lives, how the application handles scale and failure -- is not something you can prompt your way through. These decisions have long consequences. A wrong call on your data model in month one costs you in month fourteen, usually at the worst possible moment.&lt;/p&gt;

&lt;p&gt;Security is another. I've watched AI-generated authentication code that looked perfectly functional contain vulnerabilities that only became visible when someone with security experience reviewed it. AI tools don't have a threat model. They don't reason about attack surfaces. They generate code that passes a surface-level review and falls apart under scrutiny.&lt;/p&gt;

&lt;p&gt;Domain-specific business logic is the third area where AI tools genuinely struggle. Your application's rules about how a particular workflow operates, what edge cases matter, why a specific exception exists in the codebase -- that knowledge lives in your team's heads and in your commit history. AI has none of it.&lt;/p&gt;

&lt;p&gt;And then there's production. When something breaks at 2am and your customers are affected, you need a developer who owns the system and knows where to look. AI tools don't carry pagers.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Tools Full Stack Developers Are Actually Using in 2026
&lt;/h2&gt;

&lt;p&gt;The landscape has consolidated a bit from the fragmented market of 2023-2024. Most professional development teams are working with some combination of these:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; remains the most widely adopted -- 90% of Fortune 100 companies use it according to Microsoft's own reporting, which gives you a sense of how mainstream it's become. It handles inline autocomplete well and integrates cleanly with most IDEs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt; has built a strong following among individual developers and smaller teams. Its agent mode can operate across multiple files simultaneously, which makes it useful for refactoring and larger structural changes. It requires more active developer oversight on complex tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code&lt;/strong&gt; handles multi-step engineering work more thoughtfully than most tools -- it can reason about tradeoffs, work across large codebases, and flag its own uncertainty, which is actually useful. Increasingly used for architecture questions and codebase explanation.&lt;/p&gt;

&lt;p&gt;The common thread: none of these tools are autonomous. They're accelerators. The developer decides what to build, reviews what gets generated, and owns what ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision That Actually Matters
&lt;/h2&gt;

&lt;p&gt;The question most engineering leaders should be asking isn't "AI or developers?" It's "for this specific problem, which one is right?"&lt;/p&gt;

&lt;p&gt;Greenfield prototype with low complexity, tight timeline, and a team that just needs to test a hypothesis? Start with AI-assisted development. Get something in front of users. Then bring in a &lt;a href="https://www.hiddenbrains.com/hire-fullstack-developers.html" rel="noopener noreferrer"&gt;dedicated full stack development team&lt;/a&gt; to rebuild what's worth keeping on a proper foundation.&lt;/p&gt;

&lt;p&gt;Production application with real users, real data, real security requirements, and a need for ongoing iteration? You need developers who own the code. AI tools help them move faster. They don't replace the judgment.&lt;/p&gt;

&lt;p&gt;Maintenance-heavy product with a large existing codebase? AI tools help here -- code explanation, test generation, documentation -- but you still need at least one developer who knows the system well enough to validate what the tool produces.&lt;/p&gt;

&lt;p&gt;Hidden Brains has helped teams navigate this exact tradeoff across fintech, healthcare, eCommerce, and logistics. The pattern we see consistently: companies that use AI tools to speed up their developers ship better software than companies that try to replace developers with AI tools entirely. The difference isn't ideological. It's practical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What does a full stack developer do day to day?
&lt;/h3&gt;

&lt;p&gt;On any given day, a full stack developer might be designing a new database table, writing the API endpoint that reads from it, building the UI component that displays the data, and reviewing a pull request from a teammate. They're expected to move fluidly between frontend and backend concerns without needing a handoff between specialists. In 2026, most full stack roles also involve working with cloud infrastructure and, increasingly, integrating AI features directly into product functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can AI build a full stack application without a developer?
&lt;/h3&gt;

&lt;p&gt;For a very simple application -- a basic form that collects data and stores it, or a static marketing page with minor interactivity -- AI tools can get you most of the way there. For anything that needs real user authentication, third-party integrations, custom business logic, security hardening, or the ability to scale beyond a handful of users, you'll hit walls that require a developer's judgment to navigate. The code will generate. The system won't hold.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will AI replace full stack developers?
&lt;/h3&gt;

&lt;p&gt;Not in the timeframe most headlines suggest. Entry-level roles that were mostly boilerplate have been affected -- there's no honest way to say otherwise. But the developers who can design systems, review AI output critically, handle security and performance at scale, and translate business requirements into technical architecture are in more demand now, not less. Gartner's own research concludes that AI creates new engineering roles. The Bureau of Labor Statistics projects strong job growth for software developers through 2033. The job is changing shape. It's not disappearing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the best technology stack for web application development in 2026?
&lt;/h3&gt;

&lt;p&gt;There isn't one universal answer, and anyone who tells you otherwise is selling something. React or Next.js handles most frontend requirements well. For the backend, Node.js suits JavaScript-centric teams; Python with FastAPI is the better choice if your product has any data processing or AI workload; Go makes sense if raw performance at scale is a priority from the start. PostgreSQL is the default database choice for most relational data. TypeScript across the stack has become standard professional practice. The choice should fit your team's existing skills, your product's actual requirements, and your scaling timeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is full-stack AI development?
&lt;/h3&gt;

&lt;p&gt;Full-stack AI development refers to building applications where AI features are integrated throughout the product -- not bolted on as an afterthought. This might include an LLM-powered assistant in the frontend UI, a vector database backing semantic search in the backend, an ML model influencing business logic in the application layer, and AI-assisted code generation in the development workflow itself. It requires developers who understand both traditional full stack architecture and how AI components fit into it. The demand for developers fluent in both has grown considerably in 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I hire a full stack developer or a dedicated development team?
&lt;/h3&gt;

&lt;p&gt;For a focused product with a clear scope and a 3-6 month timeline, a strong senior full stack developer can often handle it. For anything with multiple parallel workstreams, complex integrations, or a need for specialization across frontend experience, backend architecture, and DevOps, a small coordinated team consistently outperforms a single generalist. The math changes depending on your timeline and risk tolerance. A dedicated team is also more resilient -- a single developer leaving mid-project is a much more serious disruption than losing one member of a five-person team.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does full stack development cost?
&lt;/h3&gt;

&lt;p&gt;Rates vary considerably. In the US market, senior full stack contractors typically run $100-180/hour. Staff augmentation through a vetted development partner generally ranges from $60-150/hour per developer and includes recruiting overhead, legal compliance, and talent replacement guarantees that direct hiring doesn't. Full-time senior developer salaries in competitive US markets range from $140,000 to $220,000+. For fixed-scope projects, pricing depends heavily on architecture complexity, third-party integration requirements, and how well-defined the specifications are before development starts.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does full stack development take?
&lt;/h3&gt;

&lt;p&gt;A realistic MVP with user authentication, core features, and basic deployment takes 8-12 weeks with a competent focused team. More complex products -- multi-tenant SaaS, marketplace platforms, products with AI features or complex integrations -- typically take 4-9 months through a first stable release. These timelines include design, development, testing, and deployment iterations. Projects that skip testing to hit a timeline usually spend that time (and more) on post-launch fixes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
