Skip to main content

Guide

qrcode vs qrcode.react vs node-qrcode 2026

Compare qrcode, qrcode.react, and node-qrcode for QR code generation in JavaScript. SVG vs PNG output, React components, server-side rendering, error.

·PkgPulse Team·
0

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-styling or react-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"
    />
  )
}
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

Featureqrcodeqrcode.reactqr-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

Community Adoption in 2026

qrcode (the qrcode package on npm) leads with approximately 2 million weekly downloads, reflecting its universal compatibility — it works in Node.js (as a CLI tool, in API routes, and in batch generation scripts) and in browsers. It is the choice when QR code generation needs to happen server-side, for example generating QR codes for ticket systems, print-ready marketing materials, or embedding in PDF reports. Its API is straightforward and well-documented after a decade of maintenance.

qrcode.react reaches approximately 1.5 million weekly downloads, serving the large market of React developers who need to display QR codes in web UIs. The QRCodeSVG component is the most commonly used export — SVG scales cleanly at any size, requires no canvas API, and integrates naturally with React's rendering model. The logo overlay feature (imageSettings) makes it the standard choice for brand-conscious QR displays without reaching for the heavier qr-code-styling package.

qr-code-styling at approximately 500,000 weekly downloads serves a premium niche: QR codes intended for marketing materials where a plain black-and-white grid is aesthetically insufficient. Its dot shape customization (rounded, classy, extra-rounded) and corner style options produce QR codes that are still machine-scannable but visually distinctive — common in retail, events, and brand campaigns. At 50KB it is the heaviest option in this comparison, but teams using it are accepting that trade-off for design flexibility.

Error Correction and Advanced Configuration

QR code generation has more configuration surface than most developers realize, and the defaults of each library make different tradeoffs between code size and scan reliability.

Error correction levels are the most important configuration option. All three libraries support QR code error correction levels L (7% damage recovery), M (15%), Q (25%), and H (30%). Higher error correction produces larger QR codes with more modules, but makes them scannable even when partially damaged or obscured by a logo overlay. For marketing QR codes with a brand logo covering the center, error correction level H is essential — without it, the logo destroys the code. For simple URL redirects where the code will be printed clearly, level M is the standard default.

Version selection (QR code size) is typically automatic — the library chooses the smallest version that can encode the given content at the specified error correction level. However, explicit version control matters for printed materials where the physical QR code size is constrained. A version 5 QR code (37×37 modules) at 300 DPI printed at 1cm × 1cm will produce modules smaller than a typical laser printer's dot pitch, causing scan failures in low-light conditions. For print use cases, requesting a larger version or ensuring the rendered output meets the 10-module quiet zone requirement is important.

node-qrcode's UTF-8 terminal output mode is genuinely useful for server-side development workflows. When building a CLI tool that displays a QR code for device pairing or authentication, rendering the QR code directly to the terminal with qrcode.toString(data, { type: 'utf8' }) avoids the need to serve an image. The terminal output uses Unicode block characters to render the code inline, scannable from most modern smartphone cameras at close range.

qrcode-react's customization API allows overriding individual module colors, which enables creative uses beyond black-and-white codes: gradient fills, dot-style modules instead of squares, and transparent backgrounds for overlay use cases. These aesthetic customizations must be tested for scannability — high-contrast modules remain essential. The library's imageSettings prop handles logo embedding with automatic quiet zone preservation.

For dynamic content (user-specific URLs, one-time tokens), always generate QR codes server-side and cache by content hash — QR code generation is CPU-bound and adds latency if done synchronously on each request.

TypeScript Integration and Accessibility

All three libraries ship TypeScript declarations, but the type quality differs. The qrcode package's types accurately reflect all output format options — the overloaded signatures for toBuffer, toString, and toDataURL ensure you get the right return type based on the type parameter. qrcode.react's types are integrated directly into the package (not in a separate @types/ package) and accurately type the QRCodeSVG and QRCodeCanvas component props, including the imageSettings overlay configuration.

Accessibility for QR codes in web interfaces requires careful attention. A QR code image rendered by qrcode.react or in an HTML img tag should include an aria-label describing what the QR code encodes — for example, "QR code linking to your account dashboard". Screen readers cannot interpret visual QR codes, so the underlying URL or content should always be available as a visible link or button alongside the QR code. For payment QR codes or authentication flows where the QR is the primary interaction, providing a fallback mechanism (manual entry, copy-paste) is essential for accessibility compliance.

Caching and Performance for Server-Side Generation

Server-side QR code generation with the qrcode package involves CPU computation for matrix construction and SVG or PNG rendering. For static content — a product page's QR code that links to a fixed URL — caching the generated output eliminates this cost after the first request. A simple in-process LRU cache keyed by the content string and configuration parameters provides effective caching for low-cardinality QR codes. For user-specific QR codes (unique invitation links, personalized URLs), CDN caching or an external cache (Redis, Cloudflare KV) with the content hash as the cache key is more appropriate. The qrcode package's toBuffer() output is deterministic for identical inputs, making content-addressed caching straightforward to implement correctly.

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.

Compare React and JavaScript utility packages on PkgPulse →

See also: React vs Vue and React vs Svelte, acorn vs @babel/parser vs espree.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.