<?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: Mubeen Khan</title>
    <description>The latest articles on DEV Community by Mubeen Khan (@mubeenkhaaan).</description>
    <link>https://dev.to/mubeenkhaaan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4155949%2F8dec5289-676e-45d3-b966-980f4271e1db.png</url>
      <title>DEV Community: Mubeen Khan</title>
      <link>https://dev.to/mubeenkhaaan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mubeenkhaaan"/>
    <language>en</language>
    <item>
      <title>WiFi QR codes are just one line of text (here is the format)</title>
      <dc:creator>Mubeen Khan</dc:creator>
      <pubDate>Wed, 21 Oct 2026 10:40:33 +0000</pubDate>
      <link>https://dev.to/mubeenkhaaan/wifi-qr-codes-are-just-one-line-of-text-here-is-the-format-5g3f</link>
      <guid>https://dev.to/mubeenkhaaan/wifi-qr-codes-are-just-one-line-of-text-here-is-the-format-5g3f</guid>
      <description>&lt;h1&gt;
  
  
  WiFi QR codes are just one line of text (here is the format)
&lt;/h1&gt;

&lt;p&gt;You have seen them on cafe tables and Airbnb welcome books: point your phone camera at the square, and you join the WiFi without typing a 24-character password. There is no magic in it. A WiFi QR code is a plain text string in a tiny, well-supported format, and once you know the format you can generate one in about ten lines of JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  The format
&lt;/h2&gt;

&lt;p&gt;The payload looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WIFI:T:WPA;S:CoffeeShop_Guest;P:flatwhite42;; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four fields, each terminated by a semicolon, and the whole thing ends with a double semicolon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;T:&lt;/code&gt; the security type. &lt;code&gt;WPA&lt;/code&gt; covers WPA/WPA2 (nearly every modern router), &lt;code&gt;WEP&lt;/code&gt; is for old equipment, and &lt;code&gt;nopass&lt;/code&gt; means an open network. With &lt;code&gt;nopass&lt;/code&gt; you simply omit the &lt;code&gt;P:&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S:&lt;/code&gt; the network name (SSID).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;P:&lt;/code&gt; the password. Leave it out for open networks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H:true&lt;/code&gt; is optional and marks a hidden network whose SSID is not broadcast.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So an open guest network is just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WIFI:T:nopass;S:Lobby_WiFi;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The part everyone forgets: escaping
&lt;/h2&gt;

&lt;p&gt;SSIDs and passwords can legally contain the exact characters the format uses as delimiters. If your network is called &lt;code&gt;Cafe; Bar&lt;/code&gt; or your password contains a colon, a naive string concatenation produces a payload phones misread. The rule: escape backslash, semicolon, comma, and colon with a backslash.&lt;/p&gt;

&lt;p&gt;Here is a small builder that gets it right:&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;function&lt;/span&gt; &lt;span class="nf"&gt;escapeWifiField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&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;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\\\&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;;&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;,&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/:/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildWifiPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encryption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WPA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encryption&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nopass&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;nopass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;encryption&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WEP&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;WEP&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;WPA&lt;/span&gt;&lt;span class="dl"&gt;'&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;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WIFI:T:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;S:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;escapeWifiField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&lt;/span&gt;&lt;span class="dl"&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;enc&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nopass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;P:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;escapeWifiField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&lt;/span&gt;&lt;span class="dl"&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;hidden&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;H:true;&lt;/span&gt;&lt;span class="dl"&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;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&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="nf"&gt;buildWifiPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CoffeeShop_Guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flatwhite42&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// WIFI:T:WPA;S:CoffeeShop_Guest;P:flatwhite42;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, feed the string to any QR encoder. In the browser, the &lt;code&gt;qrcode&lt;/code&gt; npm package renders it to a canvas or data URL in one call; nothing needs a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two practical notes
&lt;/h2&gt;

&lt;p&gt;First, a printed WiFi QR is a spare key: anyone who scans it reads your password in plain text on their device. For visitors, the smarter move is a separate guest network on your router, and share that one.&lt;/p&gt;

&lt;p&gt;Second, error correction matters for print. The default Medium level survives about 15% damage, which is fine on a table tent. For a sign by the door that will get scuffed, bump it to High.&lt;/p&gt;

&lt;p&gt;I built a free generator that handles the escaping, the hidden-network flag, and the print layout for you, including a ready-to-hang guest sign: &lt;a href="https://www.theutilityhub.online/tools/wifi-qr-generator" rel="noopener noreferrer"&gt;https://www.theutilityhub.online/tools/wifi-qr-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It runs entirely in the browser, so your network name and password never leave your device. Useful the next time someone asks "what is the WiFi password" and you would rather just point at the wall.&lt;/p&gt;

</description>
      <category>qrcode</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your mockups deserve better than the same grey lorem ipsum</title>
      <dc:creator>Mubeen Khan</dc:creator>
      <pubDate>Tue, 20 Oct 2026 10:33:17 +0000</pubDate>
      <link>https://dev.to/mubeenkhaaan/your-mockups-deserve-better-than-the-same-grey-lorem-ipsum-coo</link>
      <guid>https://dev.to/mubeenkhaaan/your-mockups-deserve-better-than-the-same-grey-lorem-ipsum-coo</guid>
      <description>&lt;p&gt;Every designer has the same Latin paragraph memorized by accident. We paste it into hero sections, cards, article bodies, then squint at the layout and call it a review. It works, but it is a missed opportunity: placeholder text is a design material, and the flavor you choose changes how a mockup reads.&lt;/p&gt;

&lt;p&gt;The classic Latin block is safe. Clients expect it, nobody reads it, and that is the point when you want feedback on spacing and hierarchy instead of words. But when the brand has a personality, matching the placeholder to the vibe makes presentations land harder. A coffee brand menu mockup filled with espresso, crema, single origin reads instantly as theirs. A playful kids brand with pirate ipsum gets a laugh and keeps the review about the design. A SaaS landing page with startup ipsum (disrupt, synergy, runway) feels like a real product page, which is exactly what you want stakeholders reacting to.&lt;/p&gt;

&lt;p&gt;I got tired of hand-editing filler text, so I built a small free generator with four themes: classic Latin, startup ipsum, coffee ipsum, and pirate ipsum. You pick the theme, set 1 to 10 paragraphs and short, medium or long sentences, then copy or download: &lt;a href="https://www.theutilityhub.online/tools/lorem-ipsum-generator" rel="noopener noreferrer"&gt;https://www.theutilityhub.online/tools/lorem-ipsum-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three habits that make placeholder text work harder:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Match paragraph count to the layout. One or two for hero sections and cards, three to five for article bodies. Generate slightly more than you need and trim.&lt;/li&gt;
&lt;li&gt;Keep the classic opener on when stakeholders expect the traditional first line. Turn it off when you want a fully fresh passage.&lt;/li&gt;
&lt;li&gt;Never ship placeholder to production. It sounds obvious until a launch checklist skips it. Search the built site for "lorem" before every deploy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Placeholder text will never be glamorous, but it is the cheapest design tool you already use. Give it a theme and your mockups start doing more of the talking.&lt;/p&gt;

</description>
      <category>design</category>
      <category>webdev</category>
      <category>ui</category>
      <category>css</category>
    </item>
    <item>
      <title>How I Shipped a Bilingual EN/ES Calculator Site on Next.js Static Generation</title>
      <dc:creator>Mubeen Khan</dc:creator>
      <pubDate>Thu, 01 Oct 2026 23:01:57 +0000</pubDate>
      <link>https://dev.to/mubeenkhaaan/how-i-shipped-a-bilingual-enes-calculator-site-on-nextjs-static-generation-4no1</link>
      <guid>https://dev.to/mubeenkhaaan/how-i-shipped-a-bilingual-enes-calculator-site-on-nextjs-static-generation-4no1</guid>
      <description>&lt;p&gt;I run UtilityHub — 12 free finance calculators + money guides, Next.js 15 on Vercel. Last week I shipped the whole thing in Spanish. Here's the architecture that made it (mostly) painless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The routing trick: one [locale] segment + middleware
&lt;/h2&gt;

&lt;p&gt;Instead of next-intl, I used a single &lt;code&gt;app/[locale]&lt;/code&gt; segment with a tiny middleware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/es/*&lt;/code&gt; → served as-is (locale = es)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/en/*&lt;/code&gt; → 308 redirect to the canonical unprefixed URL&lt;/li&gt;
&lt;li&gt;everything else → rewritten to &lt;code&gt;/en/*&lt;/code&gt; internally (URL stays clean)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;English keeps its clean URLs (&lt;code&gt;/tools/mortgage-calculator&lt;/code&gt;) — zero SEO risk to existing rankings — while Spanish gets real localized slugs (&lt;code&gt;/es/tools/calculadora-de-hipoteca&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Translations as data, not components
&lt;/h2&gt;

&lt;p&gt;All calculator copy (labels, explainers, FAQs, meta) lives in &lt;code&gt;lib/tools.es.js&lt;/code&gt;, mirroring the English &lt;code&gt;lib/tools.js&lt;/code&gt; schema. The math (&lt;code&gt;lib/calculations.js&lt;/code&gt;) is shared — translation never touches numbers. A Node schema-validation script checks all 12 entries: field parity, 4 FAQs each, numeric defaults byte-identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  hreflang without tears
&lt;/h2&gt;

&lt;p&gt;Every page emits reciprocal &lt;code&gt;en&lt;/code&gt; / &lt;code&gt;es&lt;/code&gt; / &lt;code&gt;x-default&lt;/code&gt; alternates, with slug mapping via a small &lt;code&gt;lib/slug-map.js&lt;/code&gt; (EN slug ↔ ES slug both directions).&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;59 static pages, one build, &lt;code&gt;&amp;lt;html lang&amp;gt;&lt;/code&gt; correct per locale, sitemap includes all &lt;code&gt;/es&lt;/code&gt; URLs. The Spanish mortgage calculator is live if you want to poke at it: &lt;a href="https://www.theutilityhub.online/es/tools/calculadora-de-hipoteca" rel="noopener noreferrer"&gt;https://www.theutilityhub.online/es/tools/calculadora-de-hipoteca&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full English site: &lt;a href="https://www.theutilityhub.online" rel="noopener noreferrer"&gt;https://www.theutilityhub.online&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>seo</category>
      <category>i18n</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
