X Xerobit

QR Code Size Guide — Minimum Size, Print Dimensions, and Scan Distance

QR code size determines scan reliability. Learn the minimum QR code size for print vs digital, how quiet zone affects scanning, optimal resolution for different use cases, and...

Mian Ali Khalid · · 4 min read
Use the tool
QR Code Generator
Generate QR codes for URLs, text, Wi-Fi, contact cards. Custom size, colors, error correction. Download as PNG or SVG. 100% client-side.
Open QR Code Generator →

A QR code that’s too small won’t scan reliably. The minimum scannable size depends on content complexity, error correction level, print quality, and the distance from the scanner.

Generate QR codes with the QR Code Generator.

Minimum size recommendations

Minimum size by use case:

Business card (printed):   25mm × 25mm (1 inch)  — 1 arm's length scan
Poster/flyer:              50mm × 50mm (2 inches) — 1-2 meter scan
Outdoor billboard:        200mm × 200mm (8 inches)— 5+ meter scan
Digital screen (mobile):  150px × 150px CSS        — tap/close scan
Digital screen (TV/kiosk):300px × 300px             — 1-2 meter scan

Rule of thumb:
  Minimum size (mm) = scan distance (cm) / 10
  1 meter scan distance → minimum 10mm → use 20mm for safety margin

QR code structure affects size

Minimum QR code is version 1: 21×21 modules (each "module" = one black/white square)

With more content, version increases (adds 4 modules per side):
Version 1:  21×21 modules = 441 modules total
Version 2:  25×25 modules = 625 modules total
Version 3:  29×29 modules = 841 modules total
...
Version 40: 177×177 modules

More content = larger version = needs larger physical size

Error correction levels also affect size:
  L (7% recovery):  smallest QR code for given content
  M (15% recovery): ~15% more modules
  Q (25% recovery): ~25% more modules — recommended for logo overlay
  H (30% recovery): largest — recommended for damaged environments

Generate QR codes at correct resolution

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

// For print: generate at 300 DPI equivalent
// 1 inch = 300 pixels at 300DPI
// 50mm = ~590px at 300DPI (50mm × 300/25.4 ≈ 590)

async function generatePrintQR(url, sizeInMm = 50) {
  const sizeInPx = Math.round(sizeInMm * 300 / 25.4);  // 300 DPI
  
  const dataUrl = await QRCode.toDataURL(url, {
    width: sizeInPx,
    margin: 4,          // Quiet zone: 4 modules (minimum is 4)
    errorCorrectionLevel: 'M',
    type: 'image/png',
  });
  
  return dataUrl;  // Base64 PNG at 300 DPI resolution
}

// For web display:
async function generateWebQR(url, cssSize = 200) {
  return QRCode.toDataURL(url, {
    width: cssSize * 2,  // 2× for retina displays
    margin: 4,
    errorCorrectionLevel: 'M',
  });
}

The quiet zone

The quiet zone is the blank white border around the QR code. Scanners need it to find the code boundaries:

Required quiet zone: 4 modules minimum

If your QR code is 100px wide with 21 modules:
  Each module = 100/21 ≈ 4.76px
  4-module quiet zone = 4 × 4.76 ≈ 19px on each side

Never place text, images, or other content inside the quiet zone.
When printing: ensure the white margin is maintained if cropping.

SVG QR codes (best for scalable use)

// Generate SVG for infinite scaling:
const svg = await QRCode.toString(url, {
  type: 'svg',
  margin: 4,
  errorCorrectionLevel: 'M',
  color: {
    dark: '#000000',   // Foreground
    light: '#ffffff',  // Background
  },
});

// Write to file:
import { writeFileSync } from 'fs';
writeFileSync('qr.svg', svg);

// Or use directly in HTML:
const svgBase64 = Buffer.from(svg).toString('base64');
const imgSrc = `data:image/svg+xml;base64,${svgBase64}`;

SVG QR codes can be scaled to any size without quality loss, making them ideal for print.

Error correction for logo overlay

// When adding a logo, use H level (30% recovery):
// The logo covers part of the QR code — H level can reconstruct it

async function qrWithLogo(url, logoPath) {
  const { createCanvas, loadImage } = await import('canvas');  // npm install canvas

  const size = 400;
  const qrDataUrl = await QRCode.toDataURL(url, {
    width: size,
    margin: 2,
    errorCorrectionLevel: 'H',  // High recovery for logo overlay
  });

  const canvas = createCanvas(size, size);
  const ctx = canvas.getContext('2d');

  // Draw QR code:
  const qrImage = await loadImage(qrDataUrl);
  ctx.drawImage(qrImage, 0, 0);

  // Overlay logo (max 30% of QR area):
  const logoSize = size * 0.25;  // 25% — safely within H level recovery
  const logoPos = (size - logoSize) / 2;
  const logo = await loadImage(logoPath);
  
  // White background behind logo:
  ctx.fillStyle = 'white';
  ctx.fillRect(logoPos - 5, logoPos - 5, logoSize + 10, logoSize + 10);
  ctx.drawImage(logo, logoPos, logoPos, logoSize, logoSize);

  return canvas.toDataURL('image/png');
}

Dynamic QR codes vs static

Static QR code: URL baked into the code
  - Free to generate
  - Cannot be changed after printing
  - If URL changes, reprint everything

Dynamic QR code: short URL that redirects
  - Service like bit.ly, QR.io manages the redirect
  - Update destination without reprinting
  - Track scan analytics (location, device, time)
  - Costs money for analytics features

For permanent URLs (app stores, contact info): static is fine
For marketing campaigns with changing destinations: dynamic

Scanning test checklist

  • Test with iPhone Camera app (no QR app needed)
  • Test with Android Camera app
  • Test at minimum intended scan distance
  • Test with logo overlay (if applicable)
  • Test on a phone at 50% screen brightness
  • If printed: verify quiet zone is present
  • If outdoor: test in direct sunlight

Related posts

Related tool

QR Code Generator

Generate QR codes for URLs, text, Wi-Fi, contact cards. Custom size, colors, error correction. Download as PNG or SVG. 100% client-side.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.