qrcode vs qrcode.react vs node-qrcode: QR Code Generation in JavaScript (2026)
TL;DR
qrcode (the qrcode npm package) is the most-used QR code library — works in Node.js and browsers, outputs SVG, PNG, and data URLs, with no external dependencies. qrcode.react is the React-specific library — renders a QR code as an SVG or canvas element directly in JSX, perfect for React apps. node-qrcode is actually the same package as qrcode — it's just an older alias. For React apps: use qrcode.react. For server-side PNG/SVG generation: use qrcode. For full-featured QR with logos, colors, and styles: use qr-code-styling.
Key Takeaways
- qrcode: ~2.5M weekly downloads — Node.js + browser, SVG/PNG/UTF8 output, Promise-based
- qrcode.react: ~2M weekly downloads — React component, renders directly in JSX, SVG or canvas
- node-qrcode: same package as
qrcode— alias for legacy compat - All three support error correction levels: L (7%), M (15%), Q (25%), H (30%)
- Higher error correction = larger QR code but tolerates more damage/logo overlap
- For QR codes with custom styling/logos:
qr-code-stylingorreact-qr-code
QR Code Concepts
Error Correction Levels:
L — 7% damage recovery → smallest QR code, no logo
M — 15% damage recovery → light logos possible
Q — 25% damage recovery → standard logo usage
H — 30% damage recovery → large logo overlap
QR Output Formats:
SVG → scalable, lightweight, perfect for web display
PNG → rasterized, needed for email attachments, PDF
Canvas → browser-only, real-time rendering
UTF8 → terminal-friendly ASCII art QR code
Terminal → renders QR in terminal using block characters
qrcode
qrcode — the standard QR library for Node.js and browsers:
Node.js usage (server-side)
import QRCode from "qrcode"
// Generate PNG file:
await QRCode.toFile("qr.png", "https://pkgpulse.com/compare/react-vs-vue", {
errorCorrectionLevel: "H",
margin: 4,
width: 300,
color: {
dark: "#000000",
light: "#ffffff",
},
})
// Generate PNG as Buffer:
const pngBuffer = await QRCode.toBuffer("https://pkgpulse.com", {
type: "png",
width: 400,
})
// pngBuffer is a Buffer — write to file, send as response, etc.
// Generate SVG string:
const svg = await QRCode.toString("https://pkgpulse.com", {
type: "svg",
})
// "<svg xmlns=\"http://www.w3.org/2000/svg\"...>..."
// Generate data URL (for HTML img src):
const dataUrl = await QRCode.toDataURL("https://pkgpulse.com", {
type: "image/png",
width: 300,
errorCorrectionLevel: "M",
})
// "data:image/png;base64,iVBOR..."
// Generate UTF8 for terminal output:
const ascii = await QRCode.toString("https://pkgpulse.com", {
type: "terminal",
small: true, // Use smaller blocks
})
console.log(ascii) // Prints QR as ASCII art in terminal
Express API endpoint
import express from "express"
import QRCode from "qrcode"
const app = express()
// Endpoint: GET /qr?url=https://pkgpulse.com&size=300
app.get("/qr", async (req, res) => {
const { url, size = "300", format = "png" } = req.query
if (!url || typeof url !== "string") {
return res.status(400).json({ error: "url parameter required" })
}
try {
if (format === "svg") {
const svg = await QRCode.toString(url, {
type: "svg",
errorCorrectionLevel: "M",
})
res.setHeader("Content-Type", "image/svg+xml")
return res.send(svg)
}
const buffer = await QRCode.toBuffer(url, {
type: "png",
width: Number(size),
errorCorrectionLevel: "H", // High for logos
})
res.setHeader("Content-Type", "image/png")
res.send(buffer)
} catch (err) {
res.status(500).json({ error: "QR generation failed" })
}
})
Validate QR data capacity
import QRCode from "qrcode"
// QR codes have capacity limits — validate before generating:
async function createQR(data: string, options?: QRCode.QRCodeOptions) {
try {
return await QRCode.toDataURL(data, options)
} catch (err) {
if ((err as Error).message.includes("data length")) {
throw new Error(`Data too long for QR code: ${data.length} chars`)
}
throw err
}
}
// Maximum data capacity (version 40, error correction L):
// Numeric: 7089 digits
// Alphanumeric: 4296 chars
// Binary: 2953 bytes
// For URLs, typically fine — only very long URLs approach limits
Next.js route handler (server-side QR)
// app/api/qr/route.ts
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const url = searchParams.get("url")
const format = searchParams.get("format") ?? "svg"
if (!url) {
return Response.json({ error: "url required" }, { status: 400 })
}
const QRCode = (await import("qrcode")).default
if (format === "svg") {
const svg = await QRCode.toString(url, { type: "svg", errorCorrectionLevel: "Q" })
return new Response(svg, {
headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=86400" },
})
}
const buffer = await QRCode.toBuffer(url, { type: "png", width: 300 })
return new Response(buffer, {
headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=86400" },
})
}
qrcode.react
qrcode.react — React QR component:
Basic usage
import { QRCodeSVG, QRCodeCanvas } from "qrcode.react"
// SVG output (recommended — scalable, lightweight):
function PackageQR({ packageUrl }: { packageUrl: string }) {
return (
<QRCodeSVG
value={packageUrl}
size={256}
level="H" // Error correction: L, M, Q, H
includeMargin={true}
/>
)
}
// Canvas output:
function PackageQRCanvas({ packageUrl }: { packageUrl: string }) {
return (
<QRCodeCanvas
value={packageUrl}
size={256}
level="H"
/>
)
}
Styled QR with logo
import { QRCodeSVG } from "qrcode.react"
// QR code with logo overlay:
function BrandedQR({ url }: { url: string }) {
return (
<QRCodeSVG
value={url}
size={256}
level="H" // High error correction needed for logo
includeMargin={true}
// Custom colors:
bgColor="#ffffff"
fgColor="#000000"
// Logo in center:
imageSettings={{
src: "/logo.png",
x: undefined, // Auto-center horizontally
y: undefined, // Auto-center vertically
height: 48,
width: 48,
excavate: true, // Remove QR modules under the image
}}
/>
)
}
Download as PNG
import { QRCodeCanvas } from "qrcode.react"
import { useRef } from "react"
function DownloadableQR({ url }: { url: string }) {
const canvasRef = useRef<HTMLCanvasElement>(null)
function downloadQR() {
const canvas = canvasRef.current?.querySelector("canvas")
if (!canvas) return
const link = document.createElement("a")
link.download = "qrcode.png"
link.href = canvas.toDataURL("image/png")
link.click()
}
return (
<div>
<div ref={canvasRef}>
<QRCodeCanvas value={url} size={300} level="H" />
</div>
<button onClick={downloadQR}>Download QR Code</button>
</div>
)
}
Dynamic QR (updates with input)
import { QRCodeSVG } from "qrcode.react"
import { useState } from "react"
function QRGenerator() {
const [input, setInput] = useState("https://pkgpulse.com")
return (
<div className="flex gap-8">
<div className="flex-1">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter URL or text..."
className="w-full border p-2 rounded"
/>
</div>
<div>
{input && (
<QRCodeSVG
value={input}
size={200}
level="M"
includeMargin={true}
/>
)}
</div>
</div>
)
}
qr-code-styling (for custom designs)
qr-code-styling — full custom styling beyond qrcode/qrcode.react:
import QRCodeStyling from "qr-code-styling"
// Highly customized QR code:
const qrCode = new QRCodeStyling({
width: 300,
height: 300,
data: "https://pkgpulse.com",
image: "/logo.png",
dotsOptions: {
color: "#FF8800", // PkgPulse orange
type: "rounded", // dot shape: square, dots, rounded, classy, classy-rounded, extra-rounded
},
backgroundOptions: {
color: "#ffffff",
},
cornersSquareOptions: {
type: "extra-rounded",
color: "#000000",
},
imageOptions: {
crossOrigin: "anonymous",
margin: 20,
},
})
// Download:
qrCode.download({ name: "pkgpulse-qr", extension: "svg" })
// Append to DOM:
qrCode.append(document.getElementById("qr-container")!)
Feature Comparison
| Feature | qrcode | qrcode.react | qr-code-styling |
|---|---|---|---|
| React component | ❌ | ✅ | ✅ (React wrapper) |
| Server-side | ✅ | ❌ | ❌ |
| SVG output | ✅ | ✅ | ✅ |
| PNG output | ✅ | ✅ (canvas) | ✅ |
| Logo support | ❌ | ✅ | ✅ |
| Custom dot shapes | ❌ | ❌ | ✅ |
| Custom colors | ✅ (fg/bg) | ✅ (fg/bg) | ✅ Full |
| Error correction | ✅ | ✅ | ✅ |
| TypeScript | ✅ | ✅ | ✅ |
| Bundle size | ~30KB | ~15KB | ~50KB |
When to Use Each
Choose qrcode if:
- Server-side QR generation (Node.js, Next.js API routes)
- PNG output for emails, PDFs, print materials
- CLI tools that print QR codes to terminal
- Non-React apps (vanilla JS, Vue, Svelte)
Choose qrcode.react if:
- React apps where QR renders in the component tree
- SVG output that scales with the UI
- Simple logo overlay needs
- Real-time QR that updates as user types
Choose qr-code-styling if:
- Marketing or brand-critical QR codes needing custom dot shapes
- You need the most polished, customizable QR output
- Rounded dots, classy corners, gradient fills
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on qrcode v1.x, qrcode.react v4.x, and qr-code-styling v1.x.