QR Code Generator Using Node.js: The Complete 2026 Guide
Learn how to generate QR codes with Node.js — from basic PNG files to a full Express API with customization, bulk generation, and production tips.
If you've ever needed to generate QR codes programmatically — whether for a menu system, event tickets, inventory tracking, or just embedding URLs — Node.js makes it dead simple. I've been building QR code generation into Node apps for years, and in this guide I'm going to walk you through everything from a basic one-liner to building a full QR code API endpoint you can actually ship to production.
No fluff. Just working code and the stuff I wish someone had told me before I started.
Why Generate QR Codes with Node.js?
Before we write a single line of code, let's talk about why Node.js is a solid choice for QR code generation.
Most QR code generators you find online are free web tools — and they work fine for one-off codes. But if you need to generate QR codes dynamically (think: every new user gets a unique QR code, or every product in your inventory needs a scannable label), you need a programmatic solution. Node.js gives you that, with the added benefit of running the same code on your server or as a CLI tool.
The qrcode npm package (also known as node-qrcode) is the most popular and battle-tested option. It has over a million weekly downloads, supports multiple output formats, and handles all the edge cases around error correction and encoding that you don't want to think about.
Getting Started: Installation and Setup
Let's start from zero. Create a new project directory and initialize it:
mkdir qr-generator
cd qr-generator
npm init -y
npm install qrcode
That's it. One dependency. No native modules to compile, no system-level dependencies to install. The qrcode package is pure JavaScript, which means it works anywhere Node.js runs.
Your First QR Code in 3 Lines
Here's the simplest possible example — generating a QR code as a PNG file:
const QRCode = require('qrcode');
QRCode.toFile('my-qr-code.png', 'https://grizzlypeaksoftware.com', function(err) {
if (err) throw err;
console.log('QR code saved!');
});
Run it with node index.js and you'll have a my-qr-code.png file in your project directory. Scan it with your phone and it'll open the URL. Done.
But if you're anything like me, you want to understand what's actually happening and how to customize it. Let's dig deeper.
Output Formats: PNG, SVG, Terminal, and Data URLs
The qrcode package supports several output methods, and choosing the right one depends on your use case.
Saving to a File (PNG or SVG)
const QRCode = require('qrcode');
// PNG file
QRCode.toFile('output.png', 'Hello World', {
type: 'png',
width: 400,
margin: 2,
color: {
dark: '#000000',
light: '#ffffff'
}
}, function(err) {
if (err) throw err;
console.log('PNG saved');
});
// SVG file
QRCode.toFile('output.svg', 'Hello World', {
type: 'svg'
}, function(err) {
if (err) throw err;
console.log('SVG saved');
});
When to use PNG: Most situations. It's universally supported and works great for printing, embedding in emails, and displaying on web pages.
When to use SVG: When you need the QR code to scale to any size without losing quality. Perfect for print materials like posters, business cards, or product packaging.
Generating a Data URL (Base64)
This is the one you'll use most often in web applications. Instead of writing a file to disk, you get a base64-encoded string that you can embed directly in an <img> tag:
const QRCode = require('qrcode');
async function generateDataURL() {
try {
const url = await QRCode.toDataURL('https://grizzlypeaksoftware.com', {
width: 300,
margin: 2,
color: {
dark: '#1a1a2e',
light: '#ffffff'
}
});
console.log(url);
// Output: data:image/png;base64,iVBORw0KGgo...
} catch (err) {
console.error(err);
}
}
generateDataURL();
You can drop that data URL straight into your HTML:
<img src="data:image/png;base64,iVBORw0KGgo..." alt="QR Code" />
Printing to the Terminal
This one's surprisingly useful during development:
const QRCode = require('qrcode');
QRCode.toString('https://grizzlypeaksoftware.com', { type: 'terminal', small: true }, function(err, string) {
if (err) throw err;
console.log(string);
});
This renders the QR code right in your terminal using Unicode block characters. I use this all the time for quick sanity checks during development — scan it with your phone right from the terminal.
Building a QR Code API with Express
Alright, let's build something real. Here's a complete Express.js API that generates QR codes on the fly:
const express = require('express');
const QRCode = require('qrcode');
const app = express();
app.use(express.json());
// Generate QR code as PNG image
app.get('/api/qr', async function(req, res) {
const text = req.query.text;
if (!text) {
return res.status(400).json({ error: 'Missing "text" query parameter' });
}
try {
const buffer = await QRCode.toBuffer(text, {
type: 'png',
width: parseInt(req.query.width) || 300,
margin: parseInt(req.query.margin) || 2,
errorCorrectionLevel: req.query.errorLevel || 'M',
color: {
dark: req.query.dark || '#000000',
light: req.query.light || '#ffffff'
}
});
res.type('png');
res.send(buffer);
} catch (err) {
res.status(500).json({ error: 'Failed to generate QR code' });
}
});
// Generate QR code as SVG
app.get('/api/qr/svg', async function(req, res) {
const text = req.query.text;
if (!text) {
return res.status(400).json({ error: 'Missing "text" query parameter' });
}
try {
const svg = await QRCode.toString(text, {
type: 'svg',
width: parseInt(req.query.width) || 300,
margin: parseInt(req.query.margin) || 2,
errorCorrectionLevel: req.query.errorLevel || 'M'
});
res.type('svg');
res.send(svg);
} catch (err) {
res.status(500).json({ error: 'Failed to generate QR code' });
}
});
// Generate QR code as base64 data URL (JSON response)
app.get('/api/qr/base64', async function(req, res) {
const text = req.query.text;
if (!text) {
return res.status(400).json({ error: 'Missing "text" query parameter' });
}
try {
const dataUrl = await QRCode.toDataURL(text, {
width: parseInt(req.query.width) || 300,
margin: parseInt(req.query.margin) || 2,
errorCorrectionLevel: req.query.errorLevel || 'M'
});
res.json({ dataUrl: dataUrl, text: text });
} catch (err) {
res.status(500).json({ error: 'Failed to generate QR code' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, function() {
console.log('QR Code API running on port ' + PORT);
});
Install Express and run it:
npm install express
node server.js
Now you can hit these endpoints:
http://localhost:3000/api/qr?text=Hello— returns a PNG imagehttp://localhost:3000/api/qr/svg?text=Hello— returns an SVGhttp://localhost:3000/api/qr/base64?text=Hello— returns a JSON object with the data URL
You can also pass customization parameters:
http://localhost:3000/api/qr?text=Hello&width=500&margin=4&dark=%23ff0000&errorLevel=H
Understanding Error Correction Levels
This is one of those things that most tutorials skip, but it actually matters in production.
QR codes have four error correction levels:
| Level | Recovery Capacity | Best For | |-------|------------------|----------| | L (Low) | ~7% | Clean environments, screen display | | M (Medium) | ~15% | General purpose (default) | | Q (Quartile) | ~25% | Light wear and tear expected | | H (High) | ~30% | Harsh conditions, partial obstruction |
Higher error correction means the QR code can still be scanned even if part of it is damaged or obscured. The tradeoff is that higher levels produce denser (more complex) QR codes, which means they need to be printed larger to remain scannable.
My recommendation: Use M for anything displayed on screens. Use H for anything that gets printed on physical products, especially if a logo might be placed over the center of the code.
QRCode.toFile('high-correction.png', 'https://example.com', {
errorCorrectionLevel: 'H',
width: 400
}, function(err) {
if (err) throw err;
});
Encoding Different Data Types
QR codes aren't just for URLs. Here are the most common data types you'll encode in practice.
URLs
QRCode.toDataURL('https://grizzlypeaksoftware.com');
Plain Text
QRCode.toDataURL('Employee: John Smith\nDepartment: Engineering\nID: EMP-2024-0531');
JSON Data
const data = {
orderId: 'ORD-2026-1234',
items: ['Widget A', 'Widget B'],
total: 49.99,
date: '2026-03-04'
};
QRCode.toDataURL(JSON.stringify(data));
WiFi Network Credentials
// Format: WIFI:T:<security>;S:<ssid>;P:<password>;;
const wifiString = 'WIFI:T:WPA;S:MyNetwork;P:MySecretPassword;;';
QRCode.toDataURL(wifiString);
When someone scans this QR code, their phone will automatically prompt them to join the WiFi network. This is one of my favorite practical uses — great for offices, Airbnb rentals, or restaurants.
const emailString = 'mailto:[email protected]?subject=Hello&body=I found you via QR code!';
QRCode.toDataURL(emailString);
vCard (Contact Information)
const vcard = `BEGIN:VCARD
VERSION:3.0
FN:Shane
ORG:Grizzly Peak Software
URL:https://grizzlypeaksoftware.com
EMAIL:[email protected]
END:VCARD`;
QRCode.toDataURL(vcard);
Generating QR Codes for Menus (Restaurant Use Case)
This is a huge use case — especially since the pandemic normalized QR code menus. Here's how you might build a system that generates unique QR codes for different menu URLs:
const QRCode = require('qrcode');
const fs = require('fs');
const path = require('path');
const menuPages = [
{ name: 'lunch', url: 'https://myrestaurant.com/menu/lunch' },
{ name: 'dinner', url: 'https://myrestaurant.com/menu/dinner' },
{ name: 'drinks', url: 'https://myrestaurant.com/menu/drinks' },
{ name: 'specials', url: 'https://myrestaurant.com/menu/specials' }
];
async function generateMenuQRCodes() {
const outputDir = path.join(__dirname, 'menu-qr-codes');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
for (const menu of menuPages) {
const filePath = path.join(outputDir, menu.name + '-menu-qr.png');
await QRCode.toFile(filePath, menu.url, {
width: 600,
margin: 3,
errorCorrectionLevel: 'H',
color: {
dark: '#2d3436',
light: '#ffffff'
}
});
console.log('Generated: ' + filePath);
}
console.log('All menu QR codes generated!');
}
generateMenuQRCodes();
This generates print-ready QR codes at 600px wide with high error correction — perfect for laminated table cards or printed menus.
QR Code Generator for Locations
Need to encode a geographic location so scanning the QR code opens a map? Here's how:
const QRCode = require('qrcode');
// Google Maps geo URI format
function generateLocationQR(latitude, longitude, label) {
const geoUri = 'geo:' + latitude + ',' + longitude + '?q=' + encodeURIComponent(label);
return QRCode.toDataURL(geoUri, {
width: 400,
errorCorrectionLevel: 'M'
});
}
// Example: Encode a business location
async function main() {
const dataUrl = await generateLocationQR(
61.2181,
-149.9003,
'Anchorage, Alaska'
);
console.log(dataUrl);
}
main();
The geo: URI scheme is supported by both iOS and Android. When scanned, it opens the device's default maps application and drops a pin at the coordinates.
Bulk QR Code Generation
One of the biggest advantages of generating QR codes with Node.js is automation. Here's a practical example that reads a CSV file and generates a QR code for each row:
const QRCode = require('qrcode');
const fs = require('fs');
const path = require('path');
// Simple CSV parser (for production, use the csv-parse package)
function parseCSV(content) {
const lines = content.trim().split('\n');
const headers = lines[0].split(',').map(function(h) { return h.trim(); });
return lines.slice(1).map(function(line) {
const values = line.split(',').map(function(v) { return v.trim(); });
const row = {};
headers.forEach(function(header, i) {
row[header] = values[i];
});
return row;
});
}
async function bulkGenerate(csvPath, outputDir) {
const content = fs.readFileSync(csvPath, 'utf-8');
const rows = parseCSV(content);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
let count = 0;
for (const row of rows) {
const data = JSON.stringify(row);
const fileName = (row.id || row.name || 'item-' + count) + '.png';
const filePath = path.join(outputDir, fileName);
await QRCode.toFile(filePath, data, {
width: 400,
errorCorrectionLevel: 'M'
});
count++;
}
console.log('Generated ' + count + ' QR codes in ' + outputDir);
}
// Usage
bulkGenerate('products.csv', './qr-codes');
I've used this pattern to generate hundreds of QR codes at a time for inventory labels and event badges. It runs in seconds.
Customizing Colors and Appearance
The qrcode package supports basic color customization through its options:
// Dark mode QR code
QRCode.toFile('dark-mode.png', 'https://example.com', {
width: 400,
color: {
dark: '#e0e0e0', // Light gray dots
light: '#1a1a2e' // Dark background
}
});
// Branded QR code
QRCode.toFile('branded.png', 'https://example.com', {
width: 400,
color: {
dark: '#2d6a4f', // Forest green dots
light: '#ffffff' // White background
}
});
// Transparent background
QRCode.toFile('transparent.png', 'https://example.com', {
width: 400,
color: {
dark: '#000000',
light: '#0000' // Transparent (4-char hex with 0 alpha)
}
});
A word of caution on colors: QR code scanners rely on high contrast between the dark and light modules. If you go too wild with colors, the code might not scan reliably. Always test your colored QR codes with at least three different scanner apps before rolling them out.
Is a QR Code Generator Safe? Security Considerations
This is a question I see come up a lot, and it's worth addressing directly.
The generation itself is safe. The qrcode npm package runs entirely locally — your data never leaves your server. Unlike online QR code generators (where you're essentially handing your data to a third party), generating codes with Node.js means you have complete control over the data.
However, there are some security concerns to be aware of:
Input validation matters. If you're building an API that accepts user input and generates QR codes, always validate and sanitize that input. QR codes can encode any string, including malicious URLs. Don't blindly generate QR codes from user-submitted data without checking it.
const { URL } = require('url');
function isValidUrl(string) {
try {
const url = new URL(string);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch (_) {
return false;
}
}
app.get('/api/qr', async function(req, res) {
const text = req.query.text;
// If the input looks like a URL, validate it
if (text.startsWith('http') && !isValidUrl(text)) {
return res.status(400).json({ error: 'Invalid URL' });
}
// ... generate QR code
});
Rate limiting is important. QR code generation is CPU-intensive at scale. Add rate limiting to any public-facing endpoint:
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per window
message: { error: 'Too many requests, please try again later' }
});
app.use('/api/qr', limiter);
QR Codes with No Expiration
Another common search query — and the answer is straightforward. QR codes generated with Node.js never expire. The code itself is just a visual encoding of data. There's no server callback, no tracking pixel, no time bomb.
When you generate a QR code that encodes a URL, the QR code itself will work forever. Whether the URL behind it remains active is a separate question entirely. If you point a QR code at https://yourdomain.com/promo/summer2026 and later take down that page, scanning the QR code will still work — it'll just lead to a 404.
This is actually a significant advantage over hosted QR code services that generate "dynamic" QR codes but charge a monthly fee to keep them active. With Node.js, your QR codes are permanent and free.
Comparing Popular Node.js QR Code Libraries
There are several QR code libraries available on npm. Here's how they stack up:
qrcode (node-qrcode)
This is the gold standard. It's the most downloaded, most maintained, and most feature-complete option. It supports PNG, SVG, terminal output, data URLs, and canvas rendering. It works in both Node.js and the browser. Unless you have a very specific reason to use something else, this is the one to use.
qr-code-styling
If you need heavily customized QR codes with rounded dots, custom shapes, or embedded logos, qr-code-styling is worth looking at. It requires canvas and jsdom as dependencies in Node.js, which adds some installation complexity (native compilation), but the visual results are impressive.
qr
A newer, minimal zero-dependency library. It's fast and has a clean API, but it's less proven in production and has a smaller community. Interesting for projects where bundle size matters.
qrcode-generator
A lightweight alternative that's been around for years. It's simple and gets the job done, but lacks the convenience methods (like toFile and toDataURL) that make qrcode so pleasant to work with.
My recommendation: Use qrcode unless you need the styling features of qr-code-styling. For 95% of use cases, qrcode is the right call.
Using the QR Code CLI
The qrcode package also ships with a command-line interface, which is handy for one-off generation:
# Install globally
npm install -g qrcode
# Generate a PNG
qrcode -o my-code.png "https://grizzlypeaksoftware.com"
# Generate an SVG
qrcode -t svg -o my-code.svg "https://grizzlypeaksoftware.com"
# Display in terminal
qrcode "https://grizzlypeaksoftware.com"
# Custom colors and error correction
qrcode -d 2d6a4f -e H -w 600 -o branded.png "https://grizzlypeaksoftware.com"
This is genuinely useful for quick tasks. I keep it installed globally on my dev machine.
QR Code Number Encoding
If you need to encode large numbers (inventory IDs, serial numbers, tracking numbers), QR codes have a dedicated numeric encoding mode that's more efficient than encoding numbers as text:
const QRCode = require('qrcode');
// The library automatically detects numeric-only content
// and uses the most efficient encoding mode
const serialNumber = '1234567890123456';
QRCode.toFile('serial.png', serialNumber, {
width: 300,
errorCorrectionLevel: 'M'
}, function(err) {
if (err) throw err;
console.log('Serial number QR code generated');
});
Numeric mode can encode up to 7,089 digits in a single QR code. Alphanumeric mode handles up to 4,296 characters, and byte mode supports up to 2,953 characters. The library automatically selects the most efficient encoding mode based on your input.
Performance Tips for Production
If you're generating QR codes at scale, here are some things I've learned the hard way:
Cache generated QR codes. If the same data gets requested multiple times, don't regenerate the QR code every time. Use a simple in-memory cache or Redis:
const cache = new Map();
async function getCachedQR(text, options) {
const cacheKey = text + JSON.stringify(options);
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const buffer = await QRCode.toBuffer(text, options);
cache.set(cacheKey, buffer);
return buffer;
}
Use streams for large files. Instead of buffering the entire image in memory, pipe directly to the response or file:
app.get('/api/qr/stream', function(req, res) {
res.type('png');
QRCode.toFileStream(res, req.query.text || 'default', {
width: 300,
errorCorrectionLevel: 'M'
});
});
Keep width reasonable. A 300px QR code is scannable from a phone at arm's length. Don't generate 2000px images unless you actually need them for print — it wastes CPU and memory.
Wrapping Up
Generating QR codes with Node.js is one of those satisfying development tasks where the effort-to-value ratio is incredibly high. In a few lines of code, you can build a system that generates unique, customized QR codes for virtually any use case — from restaurant menus to inventory management to contact sharing.
The qrcode npm package handles the heavy lifting, and because everything runs on your server, you don't have to worry about third-party services, monthly fees, or your data being collected.
If you found this useful, check out the rest of our Node.js guides for more practical, no-nonsense tutorials.