Performance

Benchmark

I benchmarked @ttsalpha/qrcode against the three QR libraries most React apps use: qrcode.react, react-qr-code, and qr-code-styling. I also included qrcode, the most-downloaded QR package on npm, as a headless baseline. The tests cover true cold start, SSR latency, throughput, repeated-value caching, sequential batch, bundle size, and feature completeness.

Environment: ubuntu-latest · Node.js v24.18.0 · ECL pinned to M · median / p95 / p99 · August 2026

Source: github.com/ttsalpha/qrcode-benchmark

Feature Comparison

Key capabilities across all five libraries, before we get to the numbers.

Feature@ttsalpha/qrcodeqrcode.reactqr-code-stylingreact-qr-codeqrcode
Output formatsSVG · PNGSVG · CanvasSVG · Canvas · PNGSVG onlySVG · Canvas · PNG
React component
Logo supportURL + React nodeURLURL
Auto ECL for logo✓ automanual onlymanual only
Custom dot & corner styles
Standalone string API✓ sync✓ async
SSR / Edge runtime browser only
Error correction level
QR version control
Zero dependencies✓ (0)✓ (0) (1 dep) (2 deps) (3 deps)
TypeScript built-in via @types
ESM + CJS dual export CJS only
Accessibility (aria / title)
Bundle size (gzip)7.9 KB5.9 KB13.5 KB8.3 KB8.5 KB

Unique input per render, 3 s window

Each call receives a distinct URL, so no lib can benefit from caching. Higher r/s is better.

@ttsalpha/qrcode (toSVGString)
5,795 r/s
qrcode (headless)
4,666 r/s
@ttsalpha/qrcode (React)
3,561 r/s
qrcode.react (SVG)
1,729 r/s
react-qr-code
1,046 r/s
qr-code-styling (async)
78 r/s

toSVGString reaches 5,795 r/s, which is 3.4× faster than qrcode.react and 74× faster than qr-code-styling. Even the React component path, at 3,561 r/s, outruns every other React library. It runs synchronously with no React or DOM overhead, so it fits server-side and batch workloads well.

Same input every render

Re-rendering one QR across requests or mounts, like receipts, kiosk screens, and shared links. @ttsalpha/qrcode ≥2.4 memoizes matrices in a 16-entry LRU, so this test is expected to favor it by design; it is kept separate from the cold-path tests above.

@ttsalpha/qrcode (toSVGString)
13,053 r/s
@ttsalpha/qrcode (React)
5,789 r/s
qrcode (headless)
4,017 r/s
qrcode.react (SVG)
1,521 r/s
react-qr-code
745 r/s
qr-code-styling (async)
58 r/s

With the matrix cache hitting, toSVGString reaches 13,053 r/s, about 3.2× the headless qrcode baseline and 8.6× qrcode.react. No other library caches by value. This payload is also slightly longer than test 01's, so their numbers sit a notch below their cold-path throughput.

Fresh process per round via child_process.fork

10 rounds each. Measures real Lambda / edge cold-start: import + first render with zero JIT warmup.

LibraryImport (ms)Import p951st render1st p952nd render
@ttsalpha/qrcode (toSVGString)31.48 ms31.94 ms3.551 ms4.199 ms0.815 ms
qrcode (headless)23.63 ms24.55 ms8.605 ms11.373 ms1.672 ms
@ttsalpha/qrcode (React)30.99 ms33.95 ms11.553 ms12.037 ms1.869 ms
qrcode.react29.24 ms32.22 ms14.271 ms14.646 ms4.252 ms
react-qr-code36.97 ms37.47 ms16.788 ms19.075 ms8.986 ms
qr-code-styling4.73 ms4.82 ms59.535 ms61.319 ms36.572 ms

toSVGString first-renders in 3.55 ms, which is 2.4× faster than the headless qrcode baseline (8.605 ms) and 4× faster than qrcode.react (14.271 ms). qrcode has the lightest import among full pipelines at 23.63 ms, versus our 31.48 ms since we also load React, but it gives that lead back on the first render. qr-code-styling imports fastest at 4.73 ms yet first-renders in 60 ms and stays slow at 37 ms, because its DOM-based async pipeline does not JIT-warm effectively.

Real-world payloads

12 varied payloads (short URL, long URL, vCard, numeric, WiFi, mailto, tel…), 10 rounds, p99 included.

LibraryMedian (ms)p95 (ms)p99 (ms)
@ttsalpha/qrcode (toSVGString)0.510.5560.556
@ttsalpha/qrcode (React)1.2661.4111.411
qrcode (headless)1.6261.8231.823
qrcode.react2.6342.9462.946
react-qr-code3.4184.1564.156
qr-code-styling Not SSR-safe

toSVGString is 3.2× faster than the headless qrcode baseline and 5.2× faster than qrcode.react across 12 mixed payloads. Tight p99 (0.556 ms) means latency stays predictable even with complex inputs like vCard or WiFi configs.

Burst of N renders, single thread

Node.js is single-threaded, so React renders run sequentially. Batch=100 (qr-code-styling: 20). 20 rounds (qr-code-styling: 10).

LibraryBatchMedian batch (ms)p95 batch (ms)Avg per render (ms)
@ttsalpha/qrcode (toSVGString)1004242.440.42
qrcode (headless)10071.2574.940.713
@ttsalpha/qrcode (React)10080.77104.830.808
qrcode.react100154.37158.581.544
react-qr-code100250.08258.662.501
qr-code-styling20567.65582.1628.383

toSVGString completes 100 renders in 42 ms median, or 0.42 ms per render. That is 1.7× faster than the headless qrcode baseline and 3.7× faster than qrcode.react. qr-code-styling takes 568 ms for just 20 renders; at that rate, 100 renders would take about 2,838 ms.

Custom dot shapes + corner styles

ECL=H (logo-safe), size=512 px. Only @ttsalpha/qrcode and qr-code-styling support custom styling.

@ttsalpha/qrcode (toSVGString)
0.311 ms
@ttsalpha/qrcode (React)
0.833 ms
qr-code-styling (async DOM)
11.84 ms

@ttsalpha/qrcode renders styled QR codes 38× faster than qr-code-styling, and it stays SSR-safe, sync, and DOM-free while doing it. qr-code-styling needs a browser environment (a JSDOM polyfill on Node.js/Edge) with an async API that does not scale.

qrcode.react, react-qr-code, and qrcode have no styling API.

Per-type render time

500 samples each, unique input, p99 included. Lower is better.

Data type@ttsalpha util@ttsalpha Reactqrcode.reactreact-qr-codeqrcode
Short URL0.143 ms0.531 ms0.571 ms0.998 ms0.228 ms
Numeric (20 digits)0.092 ms0.162 ms0.442 ms0.993 ms0.121 ms
AlphaNumeric0.142 ms0.211 ms0.628 ms0.957 ms0.223 ms
Unicode (Japanese)0.204 ms0.578 ms0.831 ms1.361 ms0.265 ms
Long URL (120 chars)0.523 ms0.62 ms1.824 ms3.218 ms0.653 ms
vCard0.429 ms0.518 ms1.518 ms2.705 ms0.61 ms

toSVGString wins on all 6 data types. Among competing libraries, headless qrcode is the clear runner-up, beating qrcode.react and react-qr-code on every type. The @ttsalpha React path itself even edges ahead of qrcode on alphanumeric, long URL, and vCard.

5,000 renders, unique input

Heap sampled at baseline, peak, and final. Near-zero drift across all libraries.

LibraryBaselinePeakFinalDrift
@ttsalpha/qrcode (toSVGString)45.37 MB45.39 MB45.37 MB+0.02 MB
@ttsalpha/qrcode (React)45.37 MB45.49 MB45.37 MB−0.02 MB
react-qr-code45.4 MB45.49 MB45.39 MB0 MB
qrcode.react45.43 MB45.56 MB45.37 MB−0.07 MB
qrcode (headless)45.46 MB45.58 MB45.47 MB0 MB

All libraries show excellent memory behavior. Peak stays within 0.13 MB of baseline across 5,000 renders with unique inputs. There are no signs of leaks in any library, and @ttsalpha/qrcode's 16-entry matrix cache holds steady within ±0.02 MB.

Minified + gzip, dependencies bundled

Source: bundlephobia.com. Minified + gzip, react/react-dom external, each lib's own dependencies bundled.

LibraryMin (KB)Gzip (KB)Runtime deps
qrcode.react15.95.90
@ttsalpha/qrcode17.67.90
react-qr-code22.88.32 (bundled)
qrcode22.98.53 (bundled)
qr-code-styling45.813.51 (bundled)

qrcode.react stays the smallest at 5.9 KB gzip. @ttsalpha/qrcode lands at 7.9 KB with zero dependencies and is fully tree-shakeable (sideEffects: false), so apps that only use the component don't pay for the export helpers.

Capability comparison

Feature@ttsalpha/qrcodeqrcode.reactqr-code-stylingreact-qr-codeqrcode
SVG output
Canvas output
PNG export
toSVGString() — sync, no DOM
React component
SSR / Edge runtime safe
Zero dependencies
Dot shape styles
Corner styles
Logo — image URL
Logo — any React node
Error correction level
QR version control
TypeScript built-in
ESM + CJS dual export
Accessibility (aria / title)
React 18+ support
React 16 / 17 support
Score16 / 1813 / 1811 / 189 / 188 / 18

Rankings

Category@ttsalpha/qrcodeqrcode.reactqr-code-stylingreact-qr-codeqrcode
Throughput#1#3#4#2
Repeated value#1#3#4#2
True cold start#1#3#4#2
SSR latency#1#3#4#2
Sequential batch#1#3#4#2
Styled QR#1#2
Bundle size#2#1#5#3#4
Feature score#1#2#3#4#5

Reproduce

All benchmark scripts are open source. Clone and run locally.

Main benchmark
pnpm bench # node --expose-gc benchmark.mjs
Cold start benchmark
node benchmark-coldstart.mjs

When to Choose

  • Best overall for React 18+
  • Need the fastest true cold start, 4× faster than qrcode.react in a fresh process
  • Need toSVGString for SSR, email templates, or batch generation
  • Re-render the same QR often, since the matrix cache makes repeats near-free
  • Need styled QR that works server-side
  • Need logo as any React component
  • Want zero dependencies
  • Bundle size is the primary constraint (5.9 KB gzip)
  • Targeting React 16/17 legacy projects
  • Need Canvas output alongside SVG
  • Simplest possible API is sufficient
  • Browser-only, no SSR requirement
  • Willing to accept ~38× slower styled render times
  • No strong reason to choose over the others
  • Fewest features and slowest renders among the SSR-safe React libraries
  • Hidden bundle cost from dependencies
  • Headless Node.js pipelines with no React at all
  • Need terminal / PNG-file output on the server
  • Async-only API and no styling, so plain QR codes only