How to Build a QR Code Generator and Reader Using JavaScript (Complete Guide)

Build a QR Code Generator and Reader

Typing long URLs or contact details is a boring task. It has a high possibility of making mistakes. Honestly, there's no need to type a long URL by hand anymore. Now a great solution to this problem is QR codes.

QR codes are now used everywhere, from booking event tickets, online payments, and accessing Wi-Fi to product packaging. Scanning a QR code and quickly opening a website or verifying a ticket—no time wasted 🕓.

Hi, I am Subha Naskar. In this guide, you'll learn how to build a QR code generator and reader using JavaScript. Here we'll focus on using built-in browser APIs like navigator to access the webcam.

At the end, you will have an idea for creating an HTML file that can do the following:

  • Generate a QR code from any text, URL, or structured data.
  • Read a QR code from an uploaded image.
  • Read a QR code directly from your device camera.

There will be no heavy frameworks, no complex methods, and no backend. But we'll dive deep into every concept, best practice, and maximum risk. So stay tuned with me.

Table of Contents

What is a QR Code and How It Work?

Before writing a single line of code, it helps to know what a QR code.

The year was 1994. Inside a busy factory in Japan, the production line of Toyota had workers spending their entire day scanning barcodes attached to car parts. Back then, barcodes were used. Workers scanned to quickly track the movement of parts during production.

But, traditional barcodes were a problem. They could store only a small information, and scanners needed to read them from a specific direction. This slowed down the workflow and frustrated the workers.

At that time, an engineer named Masahiro Hara worked at Denso Wave. He was tasked with solving this issue.

QR Code idea

One day, while playing a Japanese board game called Go, Masahiro noticed how the black and white patterns on the board created complex arrangements. That moment sparked the idea for the QR code.

Unlike a traditional barcode, a QR code can store much more data. QR Code (version 40) can hold up to 2,953 bytes for binary data or 4,296 alphanumeric characters.

What's inside a QR code?

  • Position Detection Patterns (orientation)
  • Alignment Patterns (fix distortion)
  • Timing Patterns (grid layout)
  • Format Information (error correction & mask)
  • Version Information (QR version)
  • Data & Error Correction (stored data)
anatomy of qr codes

There are many open-source libraries available for generating and decoding QR codes. These liberies will generate and decode the qr code for you. If you generate a QR code without using a library, the process becomes more complex.

But, it's not impossible. If you'd like, you can check out Massimo Artizzu blog posts on develop a QR Code Generator. They share amazing resources if you want to understand QR code further than this tutorial. Otherwise, if you are a beginner, I recommend using the library.

Why Build a Browser-Based Tool?

Many tutorials send user data to a remote API (like quickchart.io) to generate or decode QR codes. However, this method has some disadvantages.

  • Privacy:- Users may not want to share their data with other servers.
  • Speed:- Requests take time because they depend on the network.
  • Offline use:- The app won't work without internet.
  • Cost:- More users can mean API limits.

Running everything in the browser with libraries keeps data on the user's device and reduces server load. The only disadvantages of browser-based tool is of depending on the user's device . However, QR code tasks are lightweight and work well on most devices.

Project setup: No Dependencies

We'll create everything in one index.html file. You can choose any file name you like. There's no need for npm install, no webpack, or no node_modules. Only two CDN scripts will be used as external libraries:

  • QRCode:- for generating QR codes as images.
  • jsQR:- for decoding QR codes from image data.

Both libraries are open source. Their total size is under 50 KB zipped.

Create a new folder, then create an index.html file inside it. Open it in any browser, like Chrome, Firefox, Safari, or Edge.

Creating the HTML Structure

Here is the HTML structure we'll build upon:

<main>
  <h1>QR Code Tool</h1>
  <!-- Generator UI -->
  <!-- Reader UI -->
</main>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
<script>
  // Our JavaScript code will go here
</script>

That's the entire foundation. Now we'll add the interactive parts.

Building the QR Code Generator

We'll first start by creating a QR code generator.

Introducing the QRCode Library

The node-qrcode library has several methods:

  • QRCode.toCanvas():- Renders a <canvas> element.
  • QRCode.toDataURL():- Returns a base64-encoded image, that you can add to an <img> tag.
  • QRCode.toString():- Outputs a string of SVG or terminal characters.

We'll use toDataURL() because it's the easiest way to display.

Step by Step Implementation

Add the following HTML inside <main>:

<section class="generator">
  <h2>Generate QR Code</h2>
  <input type="text" id="qrInput" placeholder="Enter text or URL">
  <button onclick="generateQRCode()">Generate</button>
  <img id="qrImage" style="display: none;" alt="Generated QR code"/>
</section>

Now the JavaScript for QR Generation:

const qrInput = document.getElementById('qrInput');
const qrImage = document.getElementById('qrImage');

function generateQRCode() {
  const text = qrInput.value.trim();
  if (text === '') {
    alert('Please enter some text or a URL.');
    return;
  }

  QRCode.toDataURL(text, { width: 250, margin: 2 }, (err, url) => {
    if (err) {
      console.error(err);
      alert('Failed to generate QR code.');
      return;
    }
    qrImage.src = url;
    qrImage.style.display = "block";
  });
}

What's happening?

  • QRCode.toDataURL() accepts the input string, an optional configuration object, and a callback.
  • The callback receives either an error or the base64 image URL.
  • We set that URL as the src of the <img> tag.

That's it. You have a fully functional QR generator in under 10 lines of active code.

Customising Appearance (Size, Colour, Margin)

The library supports many options. Here's a better version with custom colors and error correction:

const options = {
  width: 300,
  margin: 4,
  color: {
    dark: '#1a4d8c',   // deep blue
    light: '#ffffff'   // white background
  },
  errorCorrectionLevel: 'H'  // up to 30% damage resistant
};

QRCode.toDataURL(text, options, callback);

You can also create a QR code directly on a <canvas> for more control, like allowing downloads as PNG. However, for most use cases, the image method works fine.

If you need to create many QR codes dynamically, you can use the same function inside a loop. Note that toDataURL is async function.

Building the QR Code Reader from Image Upload

Here, we'll use the jsQR library to decode QR codes.

How jsQR Works Behind the Scenes

jsQR is a JavaScript library that takes raw pixel data and scans for QR code patterns. It does not depend on canvas or external image processing libraries. The algorithm:

  1. Converts the image to greyscale.
  2. Looks for the position detection patterns.
  3. Estimates the QR code region.
  4. Extracts the bit matrix.
  5. Decodes the message using the Reed Solomon error correction.

Because it works with pixel data, you can provide frames of a video stream, screenshots, or uploaded files.

Implementation for File Upload

Add this HTML:

<section class="reader">
  <h2>Read QR Code from Image</h2>
  <input type="file" id="qrUpload" accept="image/*">
  <div id="readResult">See the result.</div>
</section>

Add a hidden <canvas> element. We'll use it to draw the uploaded image.

<canvas id="hiddenCanvas" style="display: none;"></canvas>

Now the JavaScript for QR Code Read:

const qrUpload = document.getElementById('qrUpload');
const readResult = document.getElementById('readResult');
const hiddenCanvas = document.getElementById('hiddenCanvas');
const ctx = hiddenCanvas.getContext('2d');

qrUpload.addEventListener('change', (event) => {
  const file = event.target.files[0];
  if (!file) return;

  readResult.textContent = 'Decoding image...';
  const img = new Image();
  img.onload = () => {
    hiddenCanvas.width = img.width;
    hiddenCanvas.height = img.height;
    ctx.drawImage(img, 0, 0, img.width, img.height);
    const imageData = ctx.getImageData(0, 0, img.width, img.height);
    const qrCode = jsQR(imageData.data, imageData.width, imageData.height);
    if (qrCode) {
      readResult.textContent = `${escapeHtml(qrCode.data)}`;
    } else {
      alert('No QR code found in this image.');
    }
    URL.revokeObjectURL(img.src);
  };
  img.onerror = () => {
     alert('Failed to load image.');
  };
  img.src = URL.createObjectURL(file);
});

// XSS protection
function escapeHtml(str) {
  return str.replace(/[&<>]/g, function(m) {
    if (m === '&') return '&';
    if (m === '<') return '<';
    if (m === '>') return '>';
    return m;
  });
}

Here's an explanation of the QR code reader code.

  • URL.createObjectURL(file) creates a local URL for the uploaded image.
  • We draw that image on the hidden canvas. Then, we call ctx.getImageData() to get an ImageData object.
  • jsQR() returns null if it doesn't find a code. Otherwise, it returns an object with properties such as data, location, and version.
  • Always revoke the object URL with URL.revokeObjectURL() to avoid memory leaks.
The QR code reader will require camera or file access.

Handling Large Images

The code works well for normal images, such as 2 to 5 megapixels. However, if a user uploads a 24 megapixel image, getImageData() will use a lot of memory. As a result, jsQR will scan the entire image, which can cause noticeable lag or even browser crashes.

So what is the solution? Resize the image before scanning. Set a maximum size, such as 800px. Here's a modified img.onload which reduces the size:

img.onload = () => {
  let width = img.width;
  let height = img.height;
  const MAX_SIZE = 800;
  if (width > MAX_SIZE || height > MAX_SIZE) {
    if (width > height) {
      height = (height * MAX_SIZE) / width;
      width = MAX_SIZE;
    } else {
      width = (width * MAX_SIZE) / height;
      height = MAX_SIZE;
    }
  }
  hiddenCanvas.width = width;
  hiddenCanvas.height = height;
  ctx.drawImage(img, 0, 0, width, height);
  // ... rest same
};

This reduces processing time without affecting decoding accuracy. 

How to scan QR code using camera?

If you want to scan QR codes directly from the device camera, you can use the html5-qrcode library. It manages camera permission, video stream, and continuous scanning with API.

Add the CDN:

<script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>

Add a container:

<section class="camera-reader">
  <h2>Live Scan (Camera)</h2>
  <div id="cameraScanner" style="width: 100%; max-width: 500px;"></div>
  <button id="startCameraBtn">Start Camera</button>
  <button id="stopCameraBtn" disabled>Stop Camera</button>
  <div id="cameraResult"></div>
</section>

Adding JavaScript for Live Camera:

let html5QrCode = null;
const startBtn = document.getElementById('startCameraBtn');
const stopBtn = document.getElementById('stopCameraBtn');
const cameraResult = document.getElementById('cameraResult');

startBtn.addEventListener('click', () => {
  html5QrCode = new Html5Qrcode("cameraScanner");
  const config = { fps: 10, qrbox: { width: 250, height: 250 } };
  html5QrCode.start(
    { facingMode: "environment" },
    config,
    (decodedText) => {
      cameraResult.textContent = `${escapeHtml(decodedText)}`;
    },
    (errorMessage) => {}
  ).then(() => {
    startBtn.disabled = true;
    stopBtn.disabled = false;
  }).catch(err => {
    console.error("Camera error", err);
    cameraResult.textContent = "Unable to access camera. Check permissions.";
  });
});

stopBtn.addEventListener('click', () => {
  if (html5QrCode) {
    html5QrCode.stop().then(() => {
      startBtn.disabled = false;
      stopBtn.disabled = true;
      cameraResult.textContent = "Camera stopped.";
    });
  }
});

This adds a professional touch. Users can quickly view the decoded text by pointing their camera at any QR code.

How to Secure QR Code Scanner Feature?

When displaying the decoded QR code text to the user, remove the old text before adding the new text to the DOM. We already used escapeHtml(). However, you should never set innerHTML to qrCode.data.

If you want to automatically redirect users using QR code link, make sure to validate the URL first. This helps prevent open redirect issues and protects users from being redirected to fake websites. For example:

Here, user confirmation is required before redirecting.

if (qrCode.data.startsWith('http://') || qrCode.data.startsWith('https://')) {
  if (confirm(`Redirect to ${qrCode.data}?`)) {
    window.location.href = qrCode.data;
  }
}

Similarly, always ask for user permission before redirecting mailto:, tel:, or sms: links.

How to Make the QR Code Tool Work Offline?

We use CDN scripts, so you need an internet connection to load the QR Code tool for the first time. To make the QR code tool fully functional offline, you can:

  • Download the library files and host them locally.
  • Use a service worker.

An easier way is to include libraries directly in the code. However, this is not a method for a real project. Also, you can add preconnect and preload to speed up loading.

Add these tags in <head>:

<link rel="preconnect" href="https://cdn.jsdelivr.net"> 
<link rel="preload" href="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js" as="script"> 
<link rel="preload" href="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js" as="script">

How You Can Extend This Tool

The simple generator works for any text. However, you can create specific QR codes that start phone actions. For example:

  • vCard (MECARD or VCARD format)
  • WiFi login (WIFI:S:MyNetwork;T:WPA;P:MyPassword;;)
  • Calendar event (BEGIN:VEVENT...)

You don't need any extra code. Just pass the properly formatted string to the generator.

Here is an example of WiFi QR generation.

const wifiString = `WIFI:S:${ssid};T:${encryption};P:${password};;`;
QRCode.toDataURL(wifiString, options, callback);

Live Demo: How the QR Tools Works

Here, live demo to help you see how the QR Code Tools works. Click "Run Code" to see the QR Code Tools live.

1. QR Code Generator

Users enter the required input based on their choice. For example, if they are providing a URL, they paste that link into the input field. Then click the generate button and the tool will create a QR code.

qr code generator

2. QR Code Reader

Users can scan a QR code with their camera or upload an image. The tool reads the code and displays data such as links or text.

qr code reader works

Next Step: Improvements you can add

Download as PNG/SVG

Add a button that saves the generated QR code.

History

Store previously generated texts in localStorage.

Batch generation

Accept a CSV file and generate multiple QR codes.

Custom Logo

Add a logo right in the middle of the QR code.

Conclusion

You've built a fully functional QR code generator and reader using fewer than 150 lines of JavaScript, excluding libraries. The tool works completely in the browser. It can be added to any web project, whether it's a landing page or a inventory system.

What You Learned
  • node-qrcode allow you to generate QR code as image, canvas, or SVG.
  • jsQR is a decoder that works with any image source.
  • Resize large images.
  • For live camera scanning, html5-qrcode simplifies the complexity.

Now improve your web applications with QR functionality. Your users will appreciate the speed. Enjoy every line you build QR code tool.

Happy coding! See you next project!

Post a Comment

0 Comments