Skip to main content

qrcode vs qrcode.react vs node-qrcode: QR Code Generation in JavaScript (2026)

·PkgPulse Team

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

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 →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.