Introduction
The qrcode npm package is a full-featured QR code generator that runs in both Node.js and the browser. It encodes text, URLs, or binary data into QR code images as PNG, SVG, Data URL, or terminal output. The library implements the QR code specification from Version 1 (21 x 21 modules) through Version 40 (177 x 177 modules) and supports all four error correction levels defined by the ISO/IEC 18004 standard.
QR codes appear everywhere in modern applications: mobile payment systems (Alipay, WeChat Pay, PayPal) embed transaction URLs in QR codes scanned at point-of-sale terminals; event ticketing platforms encode ticket IDs for contactless entry; Wi-Fi sharing screens encode network credentials in the WIFI:S:MyNetwork;T:WPA;P:MyPassword;; format so guests connect with a single scan; product packaging carries QR codes linking to nutritional data, manuals, or authenticity verification; and restaurant menus replaced paper with table QR codes during the pandemic, a habit that stuck.
This tutorial covers qrcode version 1.5.4, the latest stable release. All code examples are tested against this version in Node.js 18+ and modern browsers.
Installation and Setup
npm / yarn
Install the package as a project dependency:
npm install qrcode
# or
yarn add qrcodeFor CLI usage, install globally:
npm install -g qrcodeBrowser via CDN
The package ships a precompiled browser bundle. Load it from a CDN and access the global QRCode object:
<canvas id="canvas"></canvas>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/qrcode.min.js"></script>
<script>
QRCode.toCanvas(document.getElementById('canvas'), 'Hello World', function (error) {
if (error) console.error(error);
console.log('QR code generated');
});
</script>ES Module Import
In a bundled project (webpack, Vite, Rollup), import the module directly:
import QRCode from 'qrcode';
// Node.js CommonJS
const QRCode = require('qrcode');CLI Usage
The CLI generates QR codes directly from the terminal:
# Display QR code in terminal
qrcode "https://example.com"
# Save as PNG file
qrcode -o output.png "https://example.com"
# Save as SVG with custom colors
qrcode -t svg -d "#336699" -l "#FFFFFF" -o output.svg "https://example.com"
# Set error correction to High
qrcode -e H -o output.png "https://example.com"Core Features
1. Generate QR Code as Data URL
The toDataURL method produces a base64-encoded PNG data URL string. This is the most common browser use case because the result plugs directly into an <img> tag's src attribute without touching the filesystem.
import QRCode from 'qrcode';
// Promise-based
try {
const url = await QRCode.toDataURL('https://example.com', {
width: 300,
margin: 2,
errorCorrectionLevel: 'M'
});
console.log(url); // data:image/png;base64,iVBORw0KGgo...
// Use in an img tag
const img = document.createElement('img');
img.src = url;
document.body.appendChild(img);
} catch (err) {
console.error('Failed to generate QR code:', err);
}
// Callback-based
QRCode.toDataURL('https://example.com', function (err, url) {
if (err) throw err;
console.log(url);
});The default output is image/png. Pass type: 'image/jpeg' and quality: 0.8 for JPEG output with adjustable compression. JPEG files are smaller but lose the sharp edges that make QR codes scannable at small sizes, so PNG is recommended.
2. Output to Canvas Element
The toCanvas method renders directly onto an HTML5 <canvas> element. This is ideal when the QR code is part of a larger canvas composition, such as a branded poster or a ticket template.
const canvas = document.getElementById('qr-canvas');
await QRCode.toCanvas(canvas, 'https://example.com/ticket/12345', {
width: 256,
margin: 3,
color: {
dark: '#000000',
light: '#FFFFFF'
}
});
console.log('QR code rendered to canvas');
// If no canvas element is provided, toCanvas creates and returns one
const newCanvas = await QRCode.toCanvas('https://example.com');
document.body.appendChild(newCanvas);When no canvas element is passed as the first argument, toCanvas creates a new <canvas> element and returns it. The canvas can be further manipulated with the Canvas 2D API to add logos, watermarks, or borders.
3. SVG Output
SVG output produces resolution-independent vector graphics. The toString method with type: 'svg' returns an SVG string, perfect for high-DPI displays and print media.
// Generate SVG string
const svgString = await QRCode.toString('https://example.com', {
type: 'svg',
width: 200,
margin: 1,
color: {
dark: '#1a1a2e',
light: '#e6e6e6'
}
});
console.log(svgString);
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ..." ...>
// <path d="..." fill="#1a1a2e"/>
// </svg>
// Insert into DOM
document.getElementById('qr-container').innerHTML = svgString;
// Node.js: save SVG to file
import { writeFileSync } from 'fs';
writeFileSync('qrcode.svg', svgString);SVG output is the best choice for print applications because it scales to any size without pixelation. The generated SVG uses a single <path> element for all dark modules, keeping the file size small.
4. File Output (Node.js PNG/SVG)
The toFile method writes QR codes directly to the filesystem. It detects the output format from the file extension: .png for PNG and .svg for SVG.
import QRCode from 'qrcode';
// Save as PNG
await QRCode.toFile('qrcode.png', 'https://example.com', {
width: 400,
margin: 2,
errorCorrectionLevel: 'H',
color: {
dark: '#000000',
light: '#FFFFFF'
}
});
console.log('PNG file created');
// Save as SVG
await QRCode.toFile('qrcode.svg', 'https://example.com', {
type: 'svg',
width: 400,
margin: 2
});
console.log('SVG file created');
// Save as UTF-8 text representation
await QRCode.toFile('qrcode.txt', 'https://example.com', {
type: 'utf8'
});
console.log('Text file created');The toFile method is Node.js only and will throw in browser environments. For browser-based file downloads, use toDataURL and create a download link:
const dataUrl = await QRCode.toDataURL('https://example.com', { width: 400 });
const link = document.createElement('a');
link.download = 'qrcode.png';
link.href = dataUrl;
link.click();5. Error Correction Levels (L/M/Q/H)
QR codes include redundant data for error recovery. Higher correction levels make the code scannable even when partially damaged or obscured (for example, by a center logo), but increase the module count and physical size.
// Compare all four error correction levels
const levels = ['L', 'M', 'Q', 'H'];
const text = 'https://example.com/product/12345';
for (const level of levels) {
const url = await QRCode.toDataURL(text, {
errorCorrectionLevel: level,
width: 200
});
const img = document.createElement('img');
img.src = url;
img.title = `Level ${level}`;
document.getElementById('comparison').appendChild(img);
}
// Error correction capacity:
// L (Low) - recovers ~7% damage
// M (Medium) - recovers ~15% damage (default)
// Q (Quartile) - recovers ~25% damage
// H (High) - recovers ~30% damageUse Level L for maximum data density when the code will be displayed on screens. Use Level M (the default) for general-purpose applications. Use Level Q or H when printing on surfaces that may get scratched, or when placing a logo over the center of the QR code. Level H is required when a logo covers up to 30% of the code area.
6. Custom Colors and Margins
The color option controls the foreground (dark modules) and background (light modules) colors. The margin option sets the quiet zone width in modules.
// Branded QR code with custom colors
const brandedQR = await QRCode.toDataURL('https://example.com', {
width: 300,
margin: 4,
color: {
dark: '#2C3E50', // dark navy foreground
light: '#ECF0F1' // light gray background
}
});
// Dark mode QR code
const darkModeQR = await QRCode.toDataURL('https://example.com', {
width: 300,
margin: 3,
color: {
dark: '#FFFFFF', // white foreground
light: '#1A1A2E' // dark background
}
});
// Transparent background (RGBA)
const transparentQR = await QRCode.toDataURL('https://example.com', {
width: 300,
margin: 2,
color: {
dark: '#000000FF', // fully opaque black
light: '#FFFFFF00' // fully transparent
}
});Colors accept hex strings (#RRGGBB or #RRGGBBAA with alpha). Always maintain sufficient contrast between dark and light colors. A contrast ratio below 4:1 causes scan failures on older devices and cameras with lower resolution. The margin (quiet zone) should be at least 2 modules; the QR specification recommends 4.
7. Encoding Types (Numeric, Alphanumeric, Byte, Kanji)
QR codes support four encoding modes. The library auto-detects the optimal mode by default, but manual segment specification enables mixed-mode encoding for smaller codes.
import QRCode from 'qrcode';
// Auto mode (default) - library picks optimal encoding
await QRCode.toFile('auto.png', '0123456789');
// Automatically uses Numeric mode (most efficient for digits)
// Manual segments for mixed content
const segments = [
{ data: 'PRODUCT:', mode: 'alphanumeric' },
{ data: '0123456789', mode: 'numeric' },
{ data: '/details?ref=abc', mode: 'byte' }
];
await QRCode.toFile('mixed.png', segments, {
errorCorrectionLevel: 'M'
});
// Kanji encoding (requires helper)
const toSJIS = require('qrcode/helper/to-sjis');
await QRCode.toFile('kanji.png', '漢字', {
toSJISFunc: toSJIS
});
// Binary data
const binarySegment = [
{ data: Buffer.from([0xFE, 0xED, 0xCA, 0xFE]), mode: 'byte' }
];
await QRCode.toFile('binary.png', binarySegment);Encoding efficiency varies by mode. Numeric mode packs 3 digits into 10 bits, alphanumeric encodes 2 characters in 11 bits, byte mode uses 8 bits per character, and Kanji packs 2 characters into 13 bits. The auto mode splits input into optimal segments automatically, which is sufficient for most use cases. Manual segments help when encoding structured data like product codes where the format is known in advance.
Data capacity at Version 40 (maximum) by error correction level:
| Mode | Level L | Level M | Level Q | Level H |
|---|---|---|---|---|
| Numeric | 7,089 | 5,596 | 3,993 | 3,057 |
| Alphanumeric | 4,296 | 3,391 | 2,420 | 1,852 |
| Byte | 2,953 | 2,331 | 1,663 | 1,273 |
| Kanji | 1,817 | 1,435 | 1,024 | 784 |
Common Pitfalls
Data length exceeds capacity: Each QR version has a fixed maximum. Attempting to encode a 3,000-character URL at error correction level H fails because byte mode at level H caps at 1,273 characters. The library throws Error: Data too large for requested version. Solutions: shorten the data with a URL shortener, lower the error correction level, or let the library auto-select a higher version (up to 40).
Canvas size and resolution: Setting width too small for the QR version produces blurry codes. A Version 10 QR code has 57 x 57 modules; at width: 57 each module is 1 pixel, which becomes unreadable on retina displays. Use scale: 4 (default) or set width to at least 4x the module count. For printing, target 300 DPI minimum.
Encoding mode selection: Forcing mode: 'alphanumeric' on lowercase text throws an error because alphanumeric mode only supports uppercase A-Z, digits 0-9, and nine special characters (space $ % * + - . / :). Let auto mode handle mixed content unless the data format is strictly controlled.
UTF-8 and multibyte edge cases: The byte encoding mode uses ISO/IEC 8859-1 by default. Multi-byte UTF-8 characters (emoji, CJK characters outside Shift JIS) consume more bytes than expected, hitting capacity limits sooner. For Japanese text, use the Kanji mode with the toSJIS helper to nearly halve the size compared to byte mode.
Transparent background scanning issues: QR codes with light: '#FFFFFF00' (transparent) fail to scan when placed on dark or busy backgrounds. Always ensure the actual rendered background provides sufficient contrast against the dark modules.
Quiet zone removal: Setting margin: 0 removes the quiet zone entirely. While this saves space, many scanners depend on the quiet zone to locate the QR code boundary. Keep at least margin: 1, and prefer the default margin: 4 for printed codes.
Alternatives Comparison
qrcode vs qrcode-generator (qrcodegen): qrcode-generator is a lightweight pure-JavaScript implementation with no dependencies. It generates QR codes as HTML tables or <img> tags but lacks file output, SVG rendering, and CLI support. Choose qrcode-generator for minimal bundle size in browser-only projects where only basic rendering is needed. Choose qrcode for full-featured generation with multiple output formats.
qrcode vs QRCode.js (davidshimjs): QRCode.js is a browser-only library that renders to canvas or generates an <img> tag using table-based rendering as a fallback. It has not been actively maintained since 2015. qrcode supports both Node.js and the browser, has active maintenance, TypeScript definitions, and a wider range of output formats. Use qrcode for new projects.
qrcode vs node-qr-image: node-qr-image is a Node.js-only library that outputs PNG and SVG via streams. It does not support browser environments, canvas rendering, or Data URL output. qrcode covers all of node-qr-image's functionality plus browser support, CLI tooling, custom encoding segments, and Kanji mode.
Summary: For a universal QR code solution that works across Node.js and browsers with comprehensive output formats, qrcode is the most complete choice. For browser-only projects requiring the smallest possible bundle, consider qrcode-generator.
References
The official GitHub repository at github.com/soldair/node-qrcode contains the full API documentation, option reference, and encoding mode details. The npm registry page provides installation instructions and dependency information. The QR Code specification is defined in ISO/IEC 18004, and the QR Code official website at qrcode.com explains version structures and data capacity tables. The library supports the full QR code specification from Version 1 (21 x 21 modules) through Version 40 (177 x 177 modules).