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
Feature Comparison
Key capabilities across all five libraries, before we get to the numbers.
| Feature | @ttsalpha/qrcode | qrcode.react | qr-code-styling | react-qr-code | qrcode |
|---|---|---|---|---|---|
| Output formats | SVG · PNG | SVG · Canvas | SVG · Canvas · PNG | SVG only | SVG · Canvas · PNG |
| React component | ✓ | ✓ | — | ✓ | — |
| Logo support | URL + React node | URL | URL | — | — |
| Auto ECL for logo | ✓ auto | manual only | manual 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 KB | 5.9 KB | 13.5 KB | 8.3 KB | 8.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.
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.
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.
| Library | Import (ms) | Import p95 | 1st render | 1st p95 | 2nd render |
|---|---|---|---|---|---|
| @ttsalpha/qrcode (toSVGString) | 31.48 ms | 31.94 ms | 3.551 ms | 4.199 ms | 0.815 ms |
| qrcode (headless) | 23.63 ms | 24.55 ms | 8.605 ms | 11.373 ms | 1.672 ms |
| @ttsalpha/qrcode (React) | 30.99 ms | 33.95 ms | 11.553 ms | 12.037 ms | 1.869 ms |
| qrcode.react | 29.24 ms | 32.22 ms | 14.271 ms | 14.646 ms | 4.252 ms |
| react-qr-code | 36.97 ms | 37.47 ms | 16.788 ms | 19.075 ms | 8.986 ms |
| qr-code-styling | 4.73 ms | 4.82 ms | 59.535 ms | 61.319 ms | 36.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.
| Library | Median (ms) | p95 (ms) | p99 (ms) |
|---|---|---|---|
| @ttsalpha/qrcode (toSVGString) | 0.51 | 0.556 | 0.556 |
| @ttsalpha/qrcode (React) | 1.266 | 1.411 | 1.411 |
| qrcode (headless) | 1.626 | 1.823 | 1.823 |
| qrcode.react | 2.634 | 2.946 | 2.946 |
| react-qr-code | 3.418 | 4.156 | 4.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).
| Library | Batch | Median batch (ms) | p95 batch (ms) | Avg per render (ms) |
|---|---|---|---|---|
| @ttsalpha/qrcode (toSVGString) | 100 | 42 | 42.44 | 0.42 |
| qrcode (headless) | 100 | 71.25 | 74.94 | 0.713 |
| @ttsalpha/qrcode (React) | 100 | 80.77 | 104.83 | 0.808 |
| qrcode.react | 100 | 154.37 | 158.58 | 1.544 |
| react-qr-code | 100 | 250.08 | 258.66 | 2.501 |
| qr-code-styling | 20 | 567.65 | 582.16 | 28.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 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 React | qrcode.react | react-qr-code | qrcode |
|---|---|---|---|---|---|
| Short URL | 0.143 ms | 0.531 ms | 0.571 ms | 0.998 ms | 0.228 ms |
| Numeric (20 digits) | 0.092 ms | 0.162 ms | 0.442 ms | 0.993 ms | 0.121 ms |
| AlphaNumeric | 0.142 ms | 0.211 ms | 0.628 ms | 0.957 ms | 0.223 ms |
| Unicode (Japanese) | 0.204 ms | 0.578 ms | 0.831 ms | 1.361 ms | 0.265 ms |
| Long URL (120 chars) | 0.523 ms | 0.62 ms | 1.824 ms | 3.218 ms | 0.653 ms |
| vCard | 0.429 ms | 0.518 ms | 1.518 ms | 2.705 ms | 0.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.
| Library | Baseline | Peak | Final | Drift |
|---|---|---|---|---|
| @ttsalpha/qrcode (toSVGString) | 45.37 MB | 45.39 MB | 45.37 MB | +0.02 MB |
| @ttsalpha/qrcode (React) | 45.37 MB | 45.49 MB | 45.37 MB | −0.02 MB |
| react-qr-code | 45.4 MB | 45.49 MB | 45.39 MB | 0 MB |
| qrcode.react | 45.43 MB | 45.56 MB | 45.37 MB | −0.07 MB |
| qrcode (headless) | 45.46 MB | 45.58 MB | 45.47 MB | 0 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.
| Library | Min (KB) | Gzip (KB) | Runtime deps |
|---|---|---|---|
| qrcode.react | 15.9 | 5.9 | 0 |
| @ttsalpha/qrcode | 17.6 | 7.9 | 0 |
| react-qr-code | 22.8 | 8.3 | 2 (bundled) |
| qrcode | 22.9 | 8.5 | 3 (bundled) |
| qr-code-styling | 45.8 | 13.5 | 1 (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/qrcode | qrcode.react | qr-code-styling | react-qr-code | qrcode |
|---|---|---|---|---|---|
| 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 | — | ✓ | ✓ | ✓ | ✓ |
| Score | 16 / 18 | 13 / 18 | 11 / 18 | 9 / 18 | 8 / 18 |
Rankings
| Category | @ttsalpha/qrcode | qrcode.react | qr-code-styling | react-qr-code | qrcode |
|---|---|---|---|---|---|
| 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.
pnpm bench # node --expose-gc benchmark.mjsnode benchmark-coldstart.mjsWhen to Choose
- Best overall for React 18+
- Need the fastest true cold start, 4× faster than qrcode.react in a fresh process
- Need
toSVGStringfor 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