<!-- PkgPulse AI-readable guide source -->
<!-- Canonical: https://www.pkgpulse.com/guides/qrcode-vs-qrcode-react-vs-node-qrcode-qr-2026 -->
<!-- Raw Markdown: https://www.pkgpulse.com/guides/qrcode-vs-qrcode-react-vs-node-qrcode-qr-2026/raw.md -->
<!-- Source path: content/guides/qrcode-vs-qrcode-react-vs-node-qrcode-qr-2026.mdx -->

---
og_image: "/images/guides/qrcode-vs-qrcode-react-vs-node-qrcode-qr-2026.webp"
title: "qrcode vs qrcode.react vs node-qrcode 2026"
description: "Compare qrcode, qrcode.react, and node-qrcode for QR code generation in JavaScript. SVG vs PNG output, React components, server-side rendering, error."
date: "2026-03-09"
author: "PkgPulse Team"
tags: ["javascript", "typescript", "react", "nodejs"]
---

## 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](https://github.com/soldair/node-qrcode) — the standard QR library for Node.js and browsers:

### Node.js usage (server-side)

```typescript
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

```typescript
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

```typescript
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)

```typescript
// 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](https://github.com/zpao/qrcode.react) — React QR component:

### Basic usage

```tsx
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

```tsx
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

```tsx
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)

```tsx
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](https://github.com/kozakdenys/qr-code-styling) — full custom styling beyond qrcode/qrcode.react:

```typescript
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

---

## 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 →](https://www.pkgpulse.com)*

*See also: [React vs Vue](/compare/react-vs-vue) and [React vs Svelte](/compare/react-vs-svelte), [acorn vs @babel/parser vs espree](/guides/acorn-vs-babel-parser-vs-espree-javascript-ast-parsers-2026).*
