TL;DR
QR codes are two-dimensional matrix barcodes that encode data in a grid of black and white squares. Use error correction level M (15%) for most cases and level H (30%) when embedding a logo. Generate QR codes in JavaScript with the qrcode npm package or QRCode.toCanvas() in-browser. Use Python's qrcode[pil] library for server-side generation. Always export as SVG for print-quality output. WiFi codes use the WIFI:T:WPA;S:SSID;P:password;; format, and vCard codes use the standard vCard 3.0 schema. Try our free QR Code Generator to create codes instantly.
Key Takeaways
- Finder patterns β three 7Γ7 corner squares β let scanners detect orientation instantly, regardless of rotation.
- Timing patterns alternate black/white modules and help scanners determine module size and grid alignment.
- Error correction uses Reed-Solomon codes: L (7%), M (15%), Q (25%), H (30%) β higher levels sacrifice capacity for resilience.
- Browser QR generation: use
qrcodenpm package'sQRCode.toCanvas()orQRCode.toDataURL(). - Node.js terminal output:
QRCode.toString(data, {type: 'terminal'})prints a scannable code to stdout. - Python:
pip install qrcode[pil]thenqrcode.make(data).save('qr.png'). - Logo overlay: always use level H error correction; keep the logo under 25% of QR code area.
- WiFi QR syntax:
WIFI:T:WPA;S:NetworkName;P:Password;;β smartphones auto-prompt to join. - vCard QR codes: encode the full vCard 3.0 string; cameras offer to save the contact directly.
- Max capacity at level L: 7,089 numeric or 4,296 alphanumeric characters in a Version 40 (177Γ177) code.
- SVG over PNG: SVG scales perfectly to any size; raster PNG should be generated at final target resolution.
- Common use cases: URLs, payment links, restaurant menus, authentication (TOTP), event tickets, product packaging.
QR Code Structure: Finder Patterns, Timing, and Data Modules
Every QR code is a matrix of black and white squares called modules arranged on a square grid. The grid size ranges from 21Γ21 modules (Version 1) to 177Γ177 modules (Version 40). Within this grid, several functional regions work together to make the code scannable under real-world conditions.
Finder Patterns
The three large square patterns in the top-left, top-right, and bottom-left corners are called finder patterns. Each is a 7Γ7 module region: a solid 3Γ3 black square, surrounded by a 1-module white border, surrounded by a 1-module black ring. This distinctive nested-square appearance is uniquely recognizable by scanners and does not occur anywhere else in natural images.
Finder patterns serve three critical functions: (1) they tell the scanner that a QR code is present, (2) they provide three reference points that define the code's position and orientation β even if the code is upside-down or rotated β and (3) they enable perspective correction so that codes photographed at an angle can still be decoded. The deliberate absence of a finder pattern in the bottom-right corner tells the scanner which corner is which.
Separating each finder pattern from the data region is a 1-module-wide white separator. This white space prevents the finder pattern from blending into adjacent data modules.
Timing Patterns
Running horizontally between the top two finder patterns and vertically between the top-left and bottom-left finder patterns are the timing patterns: alternating single-module-wide rows and columns of black and white modules (black-white-black-white...). These patterns allow the scanner to determine the size of individual modules across the entire grid, compensating for variations in print quality, camera angle, or code size. Without timing patterns, the scanner would have to guess module boundaries.
Alignment Patterns
QR codes Version 2 and higher include alignment patterns β smaller 5Γ5 module squares (a 1Γ1 black center, white ring, black ring) placed at fixed positions throughout the code. Their role is to correct for perspective distortion when the QR code is on a curved or tilted surface. The number of alignment patterns grows with the version: Version 2 has one extra alignment pattern, while Version 40 has 46.
Format and Version Information
A 15-bit format information strip, stored redundantly in two locations near the finder patterns, encodes the error correction level and the mask pattern number. This strip is the first thing the scanner reads after locating the finder patterns.
QR codes Version 7 and above also contain 18-bit version information blocks near the top-right and bottom-left finder patterns. These tell the scanner the exact version number, which determines the grid dimensions and the positions of alignment patterns.
Data Modules and the Quiet Zone
The remaining area of the grid holds the actual data codewords and error correction codewords, arranged in an 8-module-wide zigzag pattern that avoids the fixed functional regions. A mask pattern (one of eight predefined patterns) is XOR-applied to the data modules to prevent large uniform regions of the same color, which confuse scanners.
Surrounding the entire QR code is the mandatory quiet zone β at least 4 modules of blank (white) space on every side. This border is the most frequently overlooked requirement: cropping or reducing the quiet zone is the single most common cause of scan failures in printed QR codes.
QR Code Module Layout (simplified, Version 1 β 21Γ21):
βββββββββββββββββββββββββββββββββββ β quiet zone (min 4 modules)
β βββββββββ timing βββββββββ β
β β FP 1 ββββββββββββ FP 2 β β FP = Finder Pattern (7Γ7)
β βββββββββ βββββββββ β
β β β timing = alternating black/white
β β DATA REGION β
β β β
β βββββββββ β
β β FP 3 β β
β βββββββββ β
βββββββββββββββββββββββββββββββββββ
Version β Module grid β Typical data (Level M)
1 β 21 Γ 21 β up to 14 alphanumeric chars
3 β 29 Γ 29 β up to 47 alphanumeric chars (short URL)
5 β 37 Γ 37 β up to 90 alphanumeric chars (standard URL)
10 β 57 Γ 57 β up to 219 alphanumeric chars (full vCard)
40 β 177 Γ 177 β up to 4296 alphanumeric chars (max)Error Correction Levels: L, M, Q, and H Explained
QR codes use Reed-Solomon error correction, a mathematical technique that allows a scanner to reconstruct data even when part of the code is damaged, obscured, or poorly printed. The QR standard defines four error correction levels, each identified by a letter:
| Level | Name | Recovery Capacity | Recommended Use |
|---|---|---|---|
| L | Low | Up to 7% of codewords | Maximum data capacity; clean digital display only |
| M | Medium | Up to 15% of codewords | Default for most use cases: print, web, packaging |
| Q | Quartile | Up to 25% of codewords | Industrial environments, outdoor signage, labels |
| H | High | Up to 30% of codewords | Required when embedding a logo over the QR code |
Higher error correction levels work by adding more redundant codewords to the data. These extra codewords take up modules that would otherwise hold data, so the trade-off is reduced data capacity for the same version, or a larger version required to hold the same amount of data. For example, a Version 5 QR code at level L stores up to 109 alphanumeric characters, but at level H stores only 45.
Choosing the right level: For digital display (screens, PDFs, emails), level M is sufficient. For printed materials that may experience wear β restaurant menus, business cards, event posters, product labels β use level M or Q. For QR codes printed on textured surfaces, applied as stickers, or used outdoors where dirt and UV damage are expected, use level Q or H. If you are embedding a logo, always use level H without exception.
JavaScript qrcode Library: Browser Generation
The most popular QR code library for browser JavaScript is qrcode.js (package name qrcode on npm). It works without a build step via CDN and supports all four error correction levels, custom colors, canvas rendering, and data URL output.
Via CDN (no build step)
<!-- Load from CDN -->
<script src="https://cdn.jsdelivr.net/npm/qrcode@1/build/qrcode.min.js"></script>
<canvas id="qr-canvas"></canvas>
<img id="qr-img" alt="QR Code" />
<script>
const data = 'https://viadreams.cc/en/tools/qr-code-generator';
// Render to canvas
QRCode.toCanvas(
document.getElementById('qr-canvas'),
data,
{ errorCorrectionLevel: 'M', width: 300, margin: 4 },
(err) => { if (err) console.error(err); }
);
// Or get a data URL for an <img> tag
QRCode.toDataURL(data, { errorCorrectionLevel: 'M', width: 300 })
.then(url => { document.getElementById('qr-img').src = url; })
.catch(console.error);
</script>Via npm (React / Next.js)
npm install qrcode
npm install --save-dev @types/qrcode # TypeScript'use client';
import { useEffect, useRef, useState } from 'react';
import QRCode from 'qrcode';
interface QrDisplayProps {
data: string;
errorLevel?: 'L' | 'M' | 'Q' | 'H';
size?: number;
}
export default function QrDisplay({ data, errorLevel = 'M', size = 256 }: QrDisplayProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [dataUrl, setDataUrl] = useState<string>('');
useEffect(() => {
if (!canvasRef.current || !data) return;
QRCode.toCanvas(canvasRef.current, data, {
errorCorrectionLevel: errorLevel,
width: size,
margin: 4,
color: { dark: '#000000', light: '#ffffff' },
});
// Also produce a data URL for download
QRCode.toDataURL(data, { errorCorrectionLevel: errorLevel, width: size, type: 'image/png' })
.then(setDataUrl);
}, [data, errorLevel, size]);
return (
<div>
<canvas ref={canvasRef} />
{dataUrl && (
<a href={dataUrl} download="qrcode.png">Download PNG</a>
)}
</div>
);
}SVG output in the browser
import QRCode from 'qrcode';
// Get SVG string
const svgString = await QRCode.toString('https://example.com', {
type: 'svg',
errorCorrectionLevel: 'M',
margin: 4,
});
// Inject into DOM
document.getElementById('qr-container').innerHTML = svgString;
// Or create a downloadable SVG Blob
const blob = new Blob([svgString], { type: 'image/svg+xml' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'qrcode.svg';
link.click();Node.js qrcode Package: Terminal and File Output
The same qrcode npm package works in Node.js for server-side generation, CLI tools, and build scripts. You can output QR codes directly to the terminal (useful for DevOps scripts), save as PNG files, or generate SVG strings to write to disk.
Terminal output (print to stdout)
// qr-terminal.mjs
import QRCode from 'qrcode';
const url = 'https://viadreams.cc/en/tools/qr-code-generator';
// Print as UTF-8 block characters (scannable in most terminals)
const terminalStr = await QRCode.toString(url, {
type: 'terminal',
small: true, // uses half-block characters for smaller output
errorCorrectionLevel: 'M',
});
console.log(terminalStr);
// Run: node qr-terminal.mjsSave as PNG file
// save-qr.mjs
import QRCode from 'qrcode';
import { writeFileSync } from 'fs';
const data = 'https://viadreams.cc/en/tools/qr-code-generator';
// Save to PNG file
await QRCode.toFile('output.png', data, {
errorCorrectionLevel: 'H',
width: 1000, // pixels β use large size for print quality
margin: 4,
color: {
dark: '#000000',
light: '#ffffff',
},
});
console.log('QR code saved to output.png');
// Save as SVG
const svgString = await QRCode.toString(data, {
type: 'svg',
errorCorrectionLevel: 'M',
margin: 4,
});
writeFileSync('output.svg', svgString);
console.log('QR code saved to output.svg');Batch generation in a script
// batch-qr.mjs
import QRCode from 'qrcode';
import { mkdirSync } from 'fs';
import path from 'path';
const items = [
{ id: 'table-1', url: 'https://restaurant.com/menu?table=1' },
{ id: 'table-2', url: 'https://restaurant.com/menu?table=2' },
{ id: 'table-3', url: 'https://restaurant.com/menu?table=3' },
];
mkdirSync('./qr-codes', { recursive: true });
for (const item of items) {
await QRCode.toFile(
path.join('./qr-codes', `${item.id}.png`),
item.url,
{ errorCorrectionLevel: 'M', width: 500 }
);
console.log(`Generated: ${item.id}.png`);
}
// Run: node batch-qr.mjsPython qrcode Library: Complete Guide
The qrcode Python library is the standard tool for QR generation in Python projects. It requires Pillow (PIL) for PNG output, but can also generate SVG without Pillow.
Installation
# For PNG output (includes Pillow)
pip install qrcode[pil]
# For SVG output only (no Pillow required)
pip install qrcodeBasic usage
import qrcode
from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_M, ERROR_CORRECT_Q, ERROR_CORRECT_H
# Simple one-liner
img = qrcode.make('https://viadreams.cc/en/tools/qr-code-generator')
img.save('qr_simple.png')
# Full control with QRCode object
qr = qrcode.QRCode(
version=None, # None = auto-select minimum version
error_correction=ERROR_CORRECT_M,
box_size=10, # pixels per module
border=4, # quiet zone width in modules
)
qr.add_data('https://viadreams.cc/en/tools/qr-code-generator')
qr.make(fit=True) # fit=True auto-adjusts version
img = qr.make_image(fill_color='black', back_color='white')
img.save('qr_full.png')
print(f'QR Version: {qr.version}, Data: {len(qr.data_list[0].data)} bytes')SVG output (no Pillow needed)
import qrcode
import qrcode.image.svg
# SVG with rect elements (good for CSS styling)
factory = qrcode.image.svg.SvgPathImage # or SvgImage, SvgFillImage
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M)
qr.add_data('https://viadreams.cc/en/tools/qr-code-generator')
qr.make(fit=True)
img = qr.make_image(image_factory=factory)
img.save('qr_code.svg') # saves clean, scalable SVGIn-memory bytes (for web APIs / Flask / Django)
import qrcode
import io
from flask import Flask, Response, request
app = Flask(__name__)
@app.route('/qr')
def generate_qr():
data = request.args.get('data', 'https://example.com')
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color='black', back_color='white')
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
return Response(buf.getvalue(), mimetype='image/png')
# GET /qr?data=https://viadreams.cc β returns PNGAdding a Logo to the Center of a QR Code
Embedding a brand logo in the center of a QR code is a popular design technique. It works because the error correction system can reconstruct the obscured data β but only if the logo covers less area than the recovery capacity of the error correction level. Always use level H (30% recovery).
The rule: Keep the logo under 25% of the total QR code area (to leave a safety margin below the 30% limit). For a 300Γ300 pixel QR code, the logo should be at most about 75Γ75 pixels, centered.
JavaScript / Canvas approach
import QRCode from 'qrcode';
async function generateQrWithLogo(
data: string,
logoSrc: string,
canvasElement: HTMLCanvasElement,
qrSize = 400
) {
// Step 1: Generate QR at level H (required for logo overlay)
await QRCode.toCanvas(canvasElement, data, {
errorCorrectionLevel: 'H', // MUST be H when adding a logo
width: qrSize,
margin: 4,
});
// Step 2: Load the logo image
const logo = new Image();
logo.src = logoSrc;
await new Promise<void>((resolve) => { logo.onload = () => resolve(); });
// Step 3: Draw logo over the center of the QR code
const ctx = canvasElement.getContext('2d')!;
const logoSize = Math.floor(qrSize * 0.22); // 22% of QR code size (safe under 25%)
const logoX = (qrSize - logoSize) / 2;
const logoY = (qrSize - logoSize) / 2;
// Optional: white rounded background behind logo for contrast
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.roundRect(logoX - 6, logoY - 6, logoSize + 12, logoSize + 12, 8);
ctx.fill();
// Draw the logo
ctx.drawImage(logo, logoX, logoY, logoSize, logoSize);
}
// Usage example
const canvas = document.getElementById('qr') as HTMLCanvasElement;
await generateQrWithLogo(
'https://viadreams.cc/en/tools/qr-code-generator',
'/logo.png',
canvas,
400
);Python: logo overlay with Pillow
import qrcode
from PIL import Image
def generate_qr_with_logo(data: str, logo_path: str, output_path: str, qr_size: int = 600):
# Step 1: Generate QR at level H
qr = qrcode.QRCode(
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
qr_img = qr.make_image(fill_color='black', back_color='white').convert('RGBA')
qr_img = qr_img.resize((qr_size, qr_size), Image.LANCZOS)
# Step 2: Open and resize logo to 22% of QR code size
logo = Image.open(logo_path).convert('RGBA')
logo_size = int(qr_size * 0.22)
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
# Step 3: Create white rounded background (optional but recommended)
bg_size = logo_size + 20
bg = Image.new('RGBA', (bg_size, bg_size), (255, 255, 255, 255))
# Step 4: Paste logo onto white background, center on QR code
bg.paste(logo, (10, 10), logo)
pos = ((qr_size - bg_size) // 2, (qr_size - bg_size) // 2)
qr_img.paste(bg, pos, bg)
# Save final image
qr_img.convert('RGB').save(output_path)
print(f'Saved: {output_path}')
generate_qr_with_logo(
'https://viadreams.cc/en/tools/qr-code-generator',
'logo.png',
'qr_with_logo.png'
)Styling QR Codes: Colors and Rounded Corners
QR codes do not have to be black and white. You can use custom foreground and background colors, or even render rounded corners on modules for a softer look. There are two important constraints: maintain sufficient contrast ratio (dark modules on light background, never the reverse) and never use colors so similar that a scanner cannot distinguish dark from light.
Custom colors with qrcode npm
import QRCode from 'qrcode';
// Brand-colored QR code
await QRCode.toFile('branded.png', 'https://example.com', {
errorCorrectionLevel: 'M',
width: 500,
margin: 4,
color: {
dark: '#1e3a5f', // dark blue modules (must be darker than background)
light: '#f0f9ff', // light blue background
},
});
// Transparent background (good for overlay on colored surfaces)
await QRCode.toFile('transparent.png', 'https://example.com', {
errorCorrectionLevel: 'M',
width: 500,
color: {
dark: '#000000',
light: '#00000000', // fully transparent background (alpha 00)
},
});Rounded corners with Canvas API
// Draw each module as a rounded rectangle using Canvas 2D API
import QRCode from 'qrcode';
async function roundedQR(data: string, canvas: HTMLCanvasElement) {
// Get raw bit matrix
const matrix = await QRCode.create(data, { errorCorrectionLevel: 'M' });
const modules = matrix.modules;
const size = modules.size;
const moduleSize = canvas.width / (size + 8); // 8 = 2 Γ 4 quiet zone modules
canvas.height = canvas.width;
const ctx = canvas.getContext('2d')!;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const offset = 4 * moduleSize; // quiet zone offset
const radius = moduleSize * 0.3; // 30% rounding
ctx.fillStyle = '#000000';
for (let row = 0; row < size; row++) {
for (let col = 0; col < size; col++) {
if (modules.get(row, col)) {
const x = offset + col * moduleSize;
const y = offset + row * moduleSize;
ctx.beginPath();
ctx.roundRect(x + 1, y + 1, moduleSize - 2, moduleSize - 2, radius);
ctx.fill();
}
}
}
}Color contrast rules
- The dark modules must always be darker than the light modules. Never swap the colors (light modules on dark background can cause scan failures on some devices).
- Maintain a minimum contrast ratio of 3:1 between dark and light areas (WCAG AA standard).
- Avoid using red as the only differentiator β red and green look the same to color-blind users, and some camera algorithms are insensitive to red.
- Test your styled QR code on multiple devices (iOS camera, Android camera, dedicated QR scanner apps) before deployment.
SVG vs PNG Output: When to Use Each
The choice between SVG and PNG output affects print quality, file size, browser performance, and use-case compatibility. Understanding the trade-offs helps you choose the right format for each context.
| Property | SVG | PNG |
|---|---|---|
| Scaling | Perfect at any size (vector) | Pixelates when enlarged |
| File size | Small (~5-20 KB) | Larger at high resolution |
| Print quality | Best β crisp at any DPI | Good only if generated large |
| Browser support | All modern browsers | Universal |
| Email embedding | Not supported in most clients | Works everywhere |
| CSS styling | Supported (fill, stroke, etc.) | Not supported |
| Office docs | Limited (Word, PowerPoint vary) | Best compatibility |
| Recommended for | Web, print, master source | Social media, email, Office |
Best practice workflow: Generate and save your QR code as SVG. Use the SVG directly in web pages. When you need PNG β for email newsletters, social media posts, or Word documents β export from the SVG at 1000Γ1000 pixels or larger. Never scale up a small PNG; always generate at the target size or above.
QR Codes for WiFi Networks: The WIFI: Protocol
The WiFi QR code format is a de facto standard supported by iOS 11+ and Android 10+ (Android 9 and earlier require a separate app). Scanning a WiFi QR code on a supported device shows a prompt asking whether to join the network β no typing required.
WiFi QR string format
WIFI:T:<encryption>;S:<SSID>;P:<password>;H:<hidden>;;
Field values:
T = WPA (WPA/WPA2/WPA3 β most common)
WEP (legacy, insecure β avoid if possible)
nopass (open network, no password)
S = network SSID (name), e.g. MyHomeNetwork
P = Wi-Fi password (omit field entirely for open networks)
H = true (hidden SSID β network does not broadcast its name)
false (or omit β standard visible network)
Examples:
WPA network: WIFI:T:WPA;S:HomeNetwork;P:MySecurePass123;;
Open network: WIFI:T:nopass;S:CafeGuest;;
Hidden WPA: WIFI:T:WPA;S:HiddenNet;P:pass123;H:true;;Character escaping rules
If the SSID or password contains any of these characters: \ ; , " : β you must escape them with a backslash.
// Characters that must be escaped: \ ; , " :
// Example: SSID = "Cafe;Network" β escape semicolon
WIFI:T:WPA;S:Cafe\;Network;P:pass;;
// Password with backslash: "my\pass" β double the backslash
WIFI:T:WPA;S:MyNet;P:my\\pass;;
// JavaScript helper
function wifiQrString(ssid: string, password: string, encryption = 'WPA', hidden = false) {
const escape = (s: string) => s.replace(/[\\;,"":]/g, c => '\\' + c);
const T = encryption;
const S = escape(ssid);
const P = password ? `P:${escape(password)};` : '';
const H = hidden ? 'H:true;' : '';
return `WIFI:T:${T};S:${S};${P}${H};`;
}
console.log(wifiQrString('HomeNet', 'secret;pass'));
// β WIFI:T:WPA;S:HomeNet;P:secret\;pass;;Generate WiFi QR in Python
import qrcode
import re
def wifi_qr_string(ssid: str, password: str, encryption: str = 'WPA', hidden: bool = False) -> str:
def escape(s: str) -> str:
return re.sub(r'([\\;,":?])', lambda m: '\\' + m.group(1), s)
T = encryption
S = escape(ssid)
P = f'P:{escape(password)};' if password else ''
H = 'H:true;' if hidden else ''
return f'WIFI:T:{T};S:{S};{P}{H};'
wifi_string = wifi_qr_string('HomeNetwork', 'MyP@$$word!')
print(f'WiFi string: {wifi_string}')
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M)
qr.add_data(wifi_string)
qr.make(fit=True)
img = qr.make_image(fill_color='black', back_color='white')
img.save('wifi_qr.png')
print('WiFi QR saved to wifi_qr.png')QR Codes for vCards: Digital Business Cards
vCard QR codes let people scan a code and instantly save a contact to their phone's address book. iOS and Android both recognize the vCard format natively. The QR code encodes a vCard string that begins with BEGIN:VCARD and ends with END:VCARD.
vCard 3.0 format (most compatible)
BEGIN:VCARD
VERSION:3.0
FN:Jane Smith
N:Smith;Jane;;;
ORG:DevToolBox Inc.
TITLE:Senior Developer
TEL;TYPE=WORK,VOICE:+1-415-555-0123
TEL;TYPE=CELL,VOICE:+1-415-555-0456
EMAIL;TYPE=WORK:jane@devtoolbox.com
URL:https://viadreams.cc
ADR;TYPE=WORK:;;123 Main St;San Francisco;CA;94102;USA
END:VCARDGenerate vCard QR in JavaScript
import QRCode from 'qrcode';
interface VCardData {
firstName: string;
lastName: string;
org?: string;
title?: string;
phone?: string;
email?: string;
url?: string;
address?: string;
}
function buildVCard(data: VCardData): string {
const lines = [
'BEGIN:VCARD',
'VERSION:3.0',
`FN:${data.firstName} ${data.lastName}`,
`N:${data.lastName};${data.firstName};;;`,
data.org && `ORG:${data.org}`,
data.title && `TITLE:${data.title}`,
data.phone && `TEL;TYPE=CELL,VOICE:${data.phone}`,
data.email && `EMAIL;TYPE=WORK:${data.email}`,
data.url && `URL:${data.url}`,
data.address && `ADR;TYPE=WORK:;;${data.address}`,
'END:VCARD',
].filter(Boolean) as string[];
return lines.join('\n');
}
const vCard = buildVCard({
firstName: 'Jane',
lastName: 'Smith',
org: 'DevToolBox Inc.',
title: 'Senior Developer',
phone: '+1-415-555-0123',
email: 'jane@devtoolbox.com',
url: 'https://viadreams.cc',
});
// Use error correction M β level H if adding logo
const dataUrl = await QRCode.toDataURL(vCard, {
errorCorrectionLevel: 'M',
width: 400,
});
console.log('vCard QR data URL:', dataUrl.slice(0, 60), '...');vCard size considerations
A minimal vCard (name, phone, email only) is typically 80-120 bytes β well within the capacity of a Version 3-4 QR code. A full vCard with address, title, URL, and multiple phone numbers can reach 300-400 bytes, requiring a Version 7-9 code. Keep vCard data minimal: scanners work most reliably at lower QR versions. Omit fields that are not critical for the contact.
QR Code Capacity and Data Limits
QR code capacity depends on three factors: the version (grid size), the error correction level, and the data mode (numeric, alphanumeric, binary, or Kanji). The four data modes offer different capacities for the same number of modules because they use different bit encodings.
| Version | Modules | Level L Numeric | Level M Alphanumeric | Level H Binary (bytes) |
|---|---|---|---|---|
| 1 | 21Γ21 | 41 | 17 | 7 |
| 2 | 25Γ25 | 77 | 32 | 14 |
| 3 | 29Γ29 | 127 | 53 | 24 |
| 5 | 37Γ37 | 220 | 93 | 42 |
| 10 | 57Γ57 | 652 | 271 | 122 |
| 15 | 77Γ77 | 1249 | 512 | 227 |
| 20 | 97Γ97 | 2061 | 843 | 370 |
| 25 | 117Γ117 | 3011 | 1220 | 532 |
| 40 | 177Γ177 | 7089 | 2953 | 1273 |
Data modes explained:
- Numeric mode: digits 0-9 only. Most efficient at 3.33 bits per character. Use for product codes, phone numbers.
- Alphanumeric mode: uppercase A-Z, digits 0-9, and a small set of symbols (
$%*+-./:and space). Efficient at 5.5 bits per character. Automatically selected for uppercase URLs. - Binary/Byte mode: all 256 ISO-8859-1 characters (Latin-1). Used for lowercase URLs, UTF-8 text, vCards. 8 bits per character. Mixed-case URLs like
https://example.com/Page?id=123use binary mode and take up more space than purely uppercase content. - Kanji mode: double-byte characters from the Shift JIS encoding for Japanese text. 13 bits per character, more efficient than binary for Japanese.
Practical tips for maximizing capacity: Shorten URLs with a URL shortener (reduces characters from 80+ to under 25). Use uppercase-only alphanumeric characters in URLs where possible (switches from binary to alphanumeric mode, roughly doubling effective capacity). Remove UTM parameters from QR code URLs and track analytics via server-side redirect instead.
Common Use Cases: URLs, Payments, and Authentication
QR codes are used across dozens of industries. Understanding the specific data format for each use case helps you generate codes that work correctly with standard device apps.
URL / Website links
The most common use case. Simply encode the full URL including the protocol: https://example.com/page?utm_source=qr. Tip: use a URL shortener for long URLs to reduce module density and improve scan reliability at small print sizes.
Payment QR codes
Payment QR codes encode payment data in application-specific formats. Common standards include:
- EMVCo (ISO 18004): Used by WeChat Pay, Alipay, and most Asian payment systems. Encodes a structured TLV (type-length-value) payload with merchant ID, amount, and currency.
- Bitcoin / Crypto: Format is
bitcoin:ADDRESS?amount=0.001&label=Payment. Most crypto wallets recognize thebitcoin:URI scheme natively. - PayPal.me / Stripe: Simply encode the payment URL, e.g.
https://paypal.me/username/50USD. - UPI (India): Format is
upi://pay?pa=VPA&pn=Name&am=Amount&cu=INR, recognized by BHIM, Google Pay, PhonePe.
Two-factor authentication (TOTP)
Authenticator apps (Google Authenticator, Authy, 1Password) use the otpauth:// URI format to configure TOTP secrets via QR code scanning. This is the standard way to set up 2FA for web applications.
// TOTP QR code format (RFC 6238 / Google Authenticator standard)
// otpauth://totp/LABEL?secret=SECRET&issuer=ISSUER&algorithm=SHA1&digits=6&period=30
const totpUri = encodeURI(
'otpauth://totp/DevToolBox:jane@example.com' +
'?secret=JBSWY3DPEHPK3PXP' + // base32-encoded TOTP secret
'&issuer=DevToolBox' +
'&algorithm=SHA1' +
'&digits=6' +
'&period=30'
);
// Generate QR at level M (level H if adding logo to setup screen)
import QRCode from 'qrcode';
const qrDataUrl = await QRCode.toDataURL(totpUri, {
errorCorrectionLevel: 'M',
width: 300,
});Restaurant menus and event tickets
Dynamic QR codes (redirect-URL codes) are ideal here because the destination can be changed without reprinting. Encode a short redirect URL, track scans per table or per ticket, and update the menu or event details at the redirect destination. For event tickets, QR codes typically encode a unique ticket ID that is validated against a database on scan β the QR code contains the token, not the ticket data itself.
Other common formats
// Email with subject and body pre-filled
mailto:contact@example.com?subject=QR%20Code%20Inquiry&body=Hello%2C
// SMS with pre-filled message
SMSTO:+15551234567:Hello from QR code
// Phone call
tel:+15551234567
// Map location (Google Maps)
https://www.google.com/maps?q=37.7749,-122.4194
// App Store / Play Store link
https://apps.apple.com/app/id123456789
https://play.google.com/store/apps/details?id=com.example.app
// Geo URI (lat/lng β supported by some apps)
geo:37.7749,-122.4194?q=37.7749,-122.4194(DevToolBox+HQ)QR Code Best Practices and Troubleshooting
Design and printing guidelines
- Minimum print size: 1.5cm Γ 1.5cm (about 0.6 inches) for a simple URL at error correction level M. Complex codes with more data need to be larger.
- Quiet zone: Always maintain at least 4 modules of white space on all four sides. This is where scanning most commonly fails.
- Contrast: Dark modules on light background. Minimum contrast ratio of 3:1. Never dark-on-dark or light-on-light.
- Avoid gradients: Gradients applied to QR codes can reduce contrast in some areas, causing scan failures. If using a gradient background, test thoroughly.
- Resolution: For print, export PNG at minimum 300 DPI at the final print size. A 2cm Γ 2cm print at 300 DPI requires 236 Γ 236 pixels minimum.
Common scan failure causes
- Cropped or insufficient quiet zone (most common)
- Low contrast between modules and background
- Overly distorted or skewed code (over 30-40 degrees of perspective angle)
- Too much data for the physical size β use a URL shortener
- PNG generated at low resolution and scaled up in layout software
- Logo covering more than 25-30% of the code at level H
- Glossy laminate creating glare that washes out module contrast
Testing checklist before deployment
- Scan with iOS Camera app (native, no third-party app)
- Scan with Android Camera app (Pixel and Samsung)
- Scan with a dedicated QR app (QR Scanner, Barcode Scanner)
- Test at the smallest planned print size
- Test from the maximum expected scanning distance
- Test at 45-degree angle to simulate realistic user behavior
- Verify the decoded URL or data is correct
- For WiFi codes: verify network join on both iOS and Android
Generate QR Codes Online β Free Tool
Use our free QR Code Generator to instantly create QR codes for URLs, WiFi, vCards, plain text, email, phone, and more. Supports all four error correction levels, custom colors, SVG and PNG download β no sign-up required.
Related tools: Base64 Encoder β’ URL Encoder β’ Hash Generator
Frequently Asked Questions
What are the three finder patterns and why are they in three corners only?
Finder patterns are the three 7Γ7 corner squares that let scanners detect, orient, and decode a QR code from any angle. They are placed in exactly three corners (top-left, top-right, bottom-left) so that the scanner can determine the correct reading orientation. The absence of a finder pattern in the bottom-right corner is the cue that tells the scanner which corner is which.
What error correction level should I use by default?
Use level M (15%) as the default for almost all use cases β it balances capacity with reasonable resilience to minor damage or printing imperfections. Use level H only when embedding a logo. Use level L only when you need maximum data capacity in a constrained space and the code will always be displayed on a clean digital screen.
Why does my QR code not scan on some devices?
The most common causes are: insufficient quiet zone (less than 4 modules of white space on any side), low contrast between dark and light modules, code printed too small for the amount of data it contains, or a scaled-up low-resolution PNG. Start by checking the quiet zone and regenerating the code at a larger pixel size.
Can I change a QR code after printing?
Static QR codes (the kind generated by most free tools) cannot be changed β the data is baked into the module pattern. If you need to change the destination after printing, use a dynamic QR code service that encodes a short redirect URL. You can then update the redirect target at any time without reprinting the physical code.
Is there a license cost to use QR codes?
No. The QR code standard (ISO/IEC 18004) is open and royalty-free. Denso Wave, which invented the technology, holds the patent but has committed to never enforce it. You are free to generate, print, and use QR codes in commercial and personal projects without any licensing fees.