<?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: Kris Dev</title>
    <description>The latest articles on DEV Community by Kris Dev (@codewithkrisdev).</description>
    <link>https://dev.to/codewithkrisdev</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%2F4140703%2F69a598b4-a251-4473-a4b8-4ded0e0d0bcb.png</url>
      <title>DEV Community: Kris Dev</title>
      <link>https://dev.to/codewithkrisdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codewithkrisdev"/>
    <language>en</language>
    <item>
      <title>I Built a Fully Client-Side QR Code Toolkit with Next.js 16</title>
      <dc:creator>Kris Dev</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:04:51 +0000</pubDate>
      <link>https://dev.to/codewithkrisdev/i-built-a-fully-client-side-qr-code-toolkit-with-nextjs-16-34bc</link>
      <guid>https://dev.to/codewithkrisdev/i-built-a-fully-client-side-qr-code-toolkit-with-nextjs-16-34bc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1rp21beivp21wd9dj0g0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1rp21beivp21wd9dj0g0.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;I recently launched &lt;strong&gt;QR Forge&lt;/strong&gt;, a free QR code toolkit built with &lt;strong&gt;Next.js 16, React, and TypeScript&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The main architectural rule behind the project is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The core QR workflow should stay in the browser.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;QR Forge does not need a backend to process QR payloads.&lt;/p&gt;

&lt;p&gt;Users can generate, customize, preview, batch-process, and export QR codes directly on their device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getqrforge.site/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=v1_1_showcase" rel="noopener noreferrer"&gt;&lt;strong&gt;Try QR Forge →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fully client-side?
&lt;/h2&gt;

&lt;p&gt;There are already many QR code generators on the web, so I wanted QR Forge to have a clear technical direction instead of becoming just another generator.&lt;/p&gt;

&lt;p&gt;For the core workflow, there is no real reason to upload QR data to a server.&lt;/p&gt;

&lt;p&gt;The browser can already handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QR generation&lt;/li&gt;
&lt;li&gt;styling&lt;/li&gt;
&lt;li&gt;image rendering&lt;/li&gt;
&lt;li&gt;SVG export&lt;/li&gt;
&lt;li&gt;CSV parsing&lt;/li&gt;
&lt;li&gt;ZIP creation&lt;/li&gt;
&lt;li&gt;print layout generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the architecture stays simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Input
    ↓
Browser
    ↓
QR Generation
    ↓
Preview
    ↓
Export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That also means QR payloads do not need to be sent to a QR processing backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What QR Forge currently supports
&lt;/h2&gt;

&lt;p&gt;The current version includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom colors, dots, corners, margins, and logos&lt;/li&gt;
&lt;li&gt;PNG, JPEG, WEBP, and SVG export&lt;/li&gt;
&lt;li&gt;Transparent PNG&lt;/li&gt;
&lt;li&gt;Ready-made QR templates&lt;/li&gt;
&lt;li&gt;Template preview and one-click apply&lt;/li&gt;
&lt;li&gt;Print Mode with bleed and registration marks&lt;/li&gt;
&lt;li&gt;Batch QR generation from CSV&lt;/li&gt;
&lt;li&gt;Client-side ZIP packaging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No account is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  One subtle export bug
&lt;/h2&gt;

&lt;p&gt;One issue I found during development was that changing QR margins did not always produce visibly different exported files.&lt;/p&gt;

&lt;p&gt;The cause was module-size rounding inside the QR rendering library.&lt;/p&gt;

&lt;p&gt;Nearby margin values could end up producing effectively identical geometry.&lt;/p&gt;

&lt;p&gt;The fix was to disable module rounding and make preview and export share the same rendering configuration.&lt;/p&gt;

&lt;p&gt;That gave QR Forge an important invariant:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What you see in the preview should be what you export.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Batch QR generation without uploads
&lt;/h2&gt;

&lt;p&gt;Batch generation also stays fully client-side.&lt;/p&gt;

&lt;p&gt;A CSV can look 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;name,value
homepage,https://example.com
devto,https://dev.to
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser handles the whole pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSV
 ↓
Parse locally
 ↓
Validate rows
 ↓
Generate QR codes
 ↓
Package into ZIP
 ↓
Download
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No CSV upload endpoint is involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Print Mode
&lt;/h2&gt;

&lt;p&gt;Print Mode keeps QR margin and print bleed as separate concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Registration marks
      ↓
Bleed area
      ↓
Trim area
      ↓
QR quiet zone
      ↓
QR matrix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selected QR size remains the trim size, while bleed and registration marks expand the final export.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-contained SVG export
&lt;/h2&gt;

&lt;p&gt;QR Forge also generates self-contained SVG files.&lt;/p&gt;

&lt;p&gt;External image references are rejected instead of producing exports that silently depend on remote resources.&lt;/p&gt;

&lt;p&gt;Locally uploaded logos can be embedded directly in the SVG, and the QR payload itself is not added to SVG metadata.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 16&lt;/li&gt;
&lt;li&gt;React 19&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;qr-code-styling&lt;/li&gt;
&lt;li&gt;fflate&lt;/li&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The general rule behind the project is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Client-side by default. Backend by exception.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If something can reasonably and safely happen in the browser, that is where I prefer to keep it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;For now, I’m intentionally slowing down feature development.&lt;/p&gt;

&lt;p&gt;The current focus is stability, usability, content, and learning how people actually use the tool before expanding it further.&lt;/p&gt;

&lt;p&gt;I’d especially appreciate feedback on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generator UX&lt;/li&gt;
&lt;li&gt;export quality&lt;/li&gt;
&lt;li&gt;templates&lt;/li&gt;
&lt;li&gt;Print Mode&lt;/li&gt;
&lt;li&gt;batch generation&lt;/li&gt;
&lt;li&gt;accessibility&lt;/li&gt;
&lt;li&gt;performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://getqrforge.site/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=v1_1_showcase" rel="noopener noreferrer"&gt;&lt;strong&gt;Try QR Forge →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading 👋&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>tailwindcss</category>
    </item>
  </channel>
</rss>
