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...
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 tools
- QR Code Generator — generate QR codes online
- Image Compressor — compress QR code PNGs
- Base64 Encoder — encode QR code as data URI
Related posts
- How QR Codes Work — and How to Make Ones That Actually Scan — QR codes aren't magic. Finder patterns, Reed-Solomon error correction, version s…
- Create QR Code — Generate QR Codes for URLs, Text, and Contacts — Creating a QR code takes your URL, text, or contact data and encodes it as a sca…
- Dynamic QR Code vs Static QR Code — What's the Difference? — Dynamic QR codes use a redirect URL that can be changed after printing. Static Q…
Related tool
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.