Toolmingo
Guides8 min read

What Is a QR Code and How Does It Work?

Learn how QR codes store data, the structure behind them (modules, finder patterns, error correction), version capacities, and how to generate QR codes in JavaScript, Python, Go, and PHP.

What Is a QR Code and How Does It Work?

QR codes are everywhere — on restaurant menus, product packaging, event tickets, and payment terminals. A QR code (Quick Response code) is a two-dimensional barcode that stores data as a grid of black and white squares. Unlike a traditional 1D barcode that holds ~20 digits, a QR code can store thousands of characters in a compact, machine-readable image.

This guide explains how QR codes are structured, how they encode data, and how to generate them programmatically in four languages.


Anatomy of a QR Code

A QR code is a square grid of black squares (modules) on a white background. The grid has several fixed regions with specific roles:

Region Purpose
Finder patterns Three identical 7×7 squares in three corners — the scanner uses them to detect position and orientation
Timing patterns Alternating black/white stripes between the finder patterns — establish the module grid
Alignment patterns Smaller squares inside larger QR versions — correct perspective distortion from camera angles
Format information Stores error correction level and mask pattern — duplicated to survive damage
Data modules The remaining squares — encode the actual payload using Reed-Solomon codes
Quiet zone A 4-module white border around the entire code — essential for scanner detection

The quiet zone (white border) is the most commonly violated rule when embedding QR codes in designs. If it is missing or too narrow, many scanners fail.


How QR Codes Encode Data

QR codes support four encoding modes, each optimised for a different type of content:

Mode Characters supported Bits per character Example
Numeric 0–9 3.33 12345
Alphanumeric 0–9, A–Z, space, $%*+-./: 5.5 HELLO WORLD
Byte ISO-8859-1 or UTF-8 8 https://example.com
Kanji Shift-JIS characters 13 Japanese text

A single QR code can mix modes — a URL uses Byte mode; a phone number uses Numeric mode. The encoder picks the most compact mode automatically.


Versions and Capacity

QR codes come in 40 versions. Version 1 is 21×21 modules; each subsequent version adds 4 modules per side, so Version 40 is 177×177 modules.

Capacity depends on version and error correction level:

Version Size Alphanumeric capacity Byte capacity
1 21×21 25 chars 17 chars
5 37×37 106 chars 72 chars
10 57×57 395 chars 271 chars
20 97×97 1,167 chars 858 chars
40 177×177 4,296 chars 2,953 chars

These figures are for error correction level L (lowest). Higher error correction reduces capacity because more space is reserved for redundancy.


Error Correction Levels

QR codes use Reed-Solomon error correction — the same algorithm used on CDs and DVDs. Even if part of the code is damaged, obscured, or covered by a logo, scanners can reconstruct the missing data.

Level Data recovery Module overhead Use case
L 7% Low Clean environments, maximum capacity
M 15% Medium General use (most common default)
Q 25% High Industrial, dirty environments
H 30% Highest Logos embedded in the code, heavy wear

Tip: If you are placing a logo in the centre of a QR code, use level H. The logo effectively damages the central modules; level H provides enough redundancy to recover that data.


Static vs Dynamic QR Codes

Static Dynamic
Data location Encoded directly in the image Encoded as a short URL that redirects
Editable No — changing content requires a new image Yes — change the destination without changing the image
Tracking No Yes — scans, location, device type
Size Larger for long URLs Small (short URL is always ≤30 chars)
Works offline Yes No — requires internet for the redirect
Cost Free Usually a paid service

For most developer use cases (linking to a fixed URL, embedding contact info, generating Wi-Fi credentials), static QR codes are sufficient and free.


QR Code for Common Payloads

Different payload formats trigger specific device behaviour:

# Plain URL
https://example.com

# Wi-Fi credentials (opens system Wi-Fi join dialog)
WIFI:T:WPA;S:MyNetwork;P:MyPassword;;

# Contact card (vCard)
BEGIN:VCARD
VERSION:3.0
FN:Jane Smith
TEL:+1-555-0100
EMAIL:jane@example.com
END:VCARD

# Email with subject and body
mailto:hello@example.com?subject=Hello&body=Hi%20there

# SMS
sms:+15550100?body=Hello

# Phone number (opens dialler)
tel:+15550100

# Geo coordinates (opens maps)
geo:37.7749,-122.4194

Code Examples

JavaScript (Node.js)

// npm install qrcode
import QRCode from "qrcode";

// Generate as data URL (embed in <img src="...">)
const dataUrl = await QRCode.toDataURL("https://example.com", {
  errorCorrectionLevel: "M",
  width: 300,
  margin: 4,          // quiet zone in modules
});

// Generate as PNG file
await QRCode.toFile("qr.png", "https://example.com", {
  errorCorrectionLevel: "H",
  width: 400,
  color: {
    dark: "#000000",
    light: "#ffffff",
  },
});

// Generate as SVG string (scalable, no pixelation)
const svg = await QRCode.toString("https://example.com", {
  type: "svg",
  errorCorrectionLevel: "M",
});

Python

# pip install qrcode[pil]
import qrcode
from qrcode.constants import ERROR_CORRECT_H

qr = qrcode.QRCode(
    version=None,           # auto-select smallest version that fits
    error_correction=ERROR_CORRECT_H,
    box_size=10,            # pixels per module
    border=4,               # quiet zone in modules (minimum 4)
)
qr.add_data("https://example.com")
qr.make(fit=True)           # auto-select version

img = qr.make_image(fill_color="black", back_color="white")
img.save("qr.png")

# Generate as bytes (for HTTP response or embedding)
from io import BytesIO
buffer = BytesIO()
img.save(buffer, format="PNG")
png_bytes = buffer.getvalue()

Go

// go get github.com/skip2/go-qrcode
package main

import (
    "os"
    qrcode "github.com/skip2/go-qrcode"
)

func main() {
    // Write PNG to file
    err := qrcode.WriteFile(
        "https://example.com",
        qrcode.Medium,  // error correction: Low/Medium/High/Highest
        256,            // image size in pixels
        "qr.png",
    )
    if err != nil {
        panic(err)
    }

    // Encode to PNG bytes (for HTTP handler)
    png, err := qrcode.Encode("https://example.com", qrcode.Medium, 256)
    if err != nil {
        panic(err)
    }
    os.WriteFile("qr2.png", png, 0644)
}

PHP

<?php
// composer require endroid/qr-code
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\Color\Color;

$qrCode = new QrCode(
    data: 'https://example.com',
    errorCorrectionLevel: ErrorCorrectionLevel::Medium,
    size: 300,
    margin: 10,              // quiet zone in pixels
    foregroundColor: new Color(0, 0, 0),
    backgroundColor: new Color(255, 255, 255),
);

$writer = new PngWriter();
$result = $writer->write($qrCode);

// Save to file
$result->saveToFile('qr.png');

// Output as HTTP response
header('Content-Type: ' . $result->getMimeType());
echo $result->getString();
?>

Quick Reference

Task JavaScript Python Go PHP
Install npm i qrcode pip install qrcode[pil] go get github.com/skip2/go-qrcode composer require endroid/qr-code
Save PNG QRCode.toFile() img.save("qr.png") qrcode.WriteFile() $result->saveToFile()
Get bytes QRCode.toBuffer() BytesIO() qrcode.Encode() $result->getString()
Data URL QRCode.toDataURL() base64.b64encode(bytes) base64 encode bytes base64_encode($bytes)
SVG output type: "svg" qrcode.image_factory=SvgImage SvgWriter
Error level errorCorrectionLevel: "H" ERROR_CORRECT_H qrcode.Highest ErrorCorrectionLevel::High

Common Pitfalls

No quiet zone. The white border around the code must be at least 4 modules wide. Without it, most phone cameras fail to detect the code. Always set margin: 4 (or equivalent) in your generator.

Low contrast colours. QR scanners expect high contrast between dark and light modules. Dark-on-dark or light-on-light colour schemes fail. Stick to near-black on near-white, or verify with multiple devices before deploying.

Logo too large with wrong error level. A logo covering more than 30% of the code cannot be recovered even with level H (which covers 30%). Keep logos to under 25% of the total area and always test with a real scanner.

URL too long for small print. A 500-character URL at Version 10 produces a dense 57×57 grid that scans poorly when printed under ~2 cm. Use a URL shortener, or switch to a dynamic QR code pointing to a short redirect URL.

Missing https:// prefix. Plain domain names like example.com are not automatically treated as URLs by all operating systems. Always include the protocol: https://example.com.

Raster scaling. Scaling a PNG QR code up causes pixelation and can introduce interpolated grey pixels that confuse scanners. Generate at final display size, or use SVG output for scalable print media.


Frequently Asked Questions

What is the minimum printable size for a QR code?
For a Version 5 code (37×37 modules), each module must be at least 0.25 mm × 0.25 mm for reliable scanning — roughly 9 mm × 9 mm total. Version 1 codes can go smaller. In practice, 2 cm × 2 cm works reliably for standard use; below that, test with multiple devices.

Can QR codes expire?
Static QR codes never expire — the data is encoded in the image itself and does not depend on any server. Dynamic QR codes (which encode a short URL) expire only if the redirect service deactivates the link.

How do I put a logo inside a QR code?
Use error correction level H (30% recovery), place the logo in the centre, keep it under 25% of the total area, and always verify the code scans correctly. Some generators (endroid/qr-code, qrcode-rust) support this natively.

Why does my QR code look different from another generator's output for the same URL?
QR encoders have freedom in choosing: version, mask pattern (8 possibilities), module placement order, and encoding mode mix. Two encoders can produce visually different but equally valid codes for the same data.

Are QR codes secure?
QR codes themselves have no security — they are data containers. Anyone can create a QR code that points to a malicious URL. Educate users to preview the URL before opening, especially for unknown QR codes in public spaces.

What's the difference between QR Code and Data Matrix?
Both are 2D barcodes, but Data Matrix is used mainly in industrial/medical contexts (smaller at tiny sizes, mandatory for GS1-128 pharmaceutical labels). QR Code dominates consumer applications due to broader device support and higher capacity.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools