QR Code
A QR code encoded in the browser and drawn as SVG. No dependency and no network round trip — which matters, because the two things people encode most are a TOTP secret and a wallet address.
src/components/ui/qr-code.tsxAbout
From the component's own source, where it was written.
A QR code, encoded here and drawn as SVG.
**Nothing leaves the page.** The common shortcut is an <img> pointed at a QR-as-a-service URL, which means the two things people most often encode — a TOTP enrolment secret and a wallet address — get handed to a third party and written into someone's access log. The encoder is @/lib/qr; see it for why that is worth 200 lines.
**SVG, not canvas.** A QR is a grid of squares, which is what vectors are for: it stays sharp at any size and when printed, it costs no raster memory at 512px, and it survives a server render. Canvas would need an effect, a ref, and a device-pixel-ratio dance to look right on a retina screen.
**The quiet zone is not decoration.** Four modules of margin are part of the spec, and a code butted against a coloured background is the single most common reason a QR will not scan. It is included by default and quietZone only makes it larger.
Contrast is the other scanning failure: dark must be darker than light, and inverting them (light code on a dark card) defeats most scanners. The defaults are near-black on white regardless of theme, deliberately — this is one of the few components that should not follow a dark mode.
Install
npx astralyx-ui add qr-code
Usage
import { QrCode } from '@/components/ui/qr-code'
<QrCode value="https://ui.astralyx.dev" level="M" size={160} />Composer
Change the props and the source below follows.
<QrCode
value="https://ui.astralyx.dev"
level="M"
size={180}
/>Examples
A TOTP enrolment code
<QrCode value={otpauthUrl} level="M" size={180} />Error correction levels
<QrCode value={url} level="L" /> {/* … M, Q, H */}API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
QrCode
Extends Omit<ComponentProps<'svg'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | required | Encoded in byte mode, versions 1–10 — 271 bytes at level L, 213 at M. Past that it throws rather than truncating a secret. |
| level | EccLevel | 'M' | Higher recovers more damage and holds less. H is worth it when a logo covers the middle or it is printed on something that creases. |
| size | number | 160 | Rendered size in pixels. The SVG scales, so this is a hint, not a raster. |
| quietZone | number | 4 | Four modules is the spec minimum, and a code butted against a coloured background is the most common reason a QR will not scan. |
| darkColor | string | '#000000' | Near-black on white regardless of theme, deliberately. Inverting them defeats most scanners. |
| lightColor | string | '#ffffff' | Near-black on white regardless of theme, deliberately. Inverting them defeats most scanners. |
| logo | React.ReactNode | — | Centred over the code. Needs level="H" to stay scannable. |
| logoSize | number | 0.22 | Fraction of the code the logo may cover. Past ~0.25 it stops scanning. |
| label | string | 'QR code' | Accessible name. The encoded value is not read out — it is rarely useful. |
| fallback | React.ReactNode | — | Rendered instead of the code when the value is too long to encode. |
| mask | all eight scored | — | The real spec, not a fixed pattern. Scanners genuinely fail on a bad mask, and "mask 0 always" makes a code that reads on one phone and not another. |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| nothing leaves the page | the whole point | The usual shortcut is an <img> at a QR-as-a-service URL, which hands a TOTP secret or a payment address to a third party and writes it into their access log. |
| SVG, not canvas | stays sharp | Sharp at any size and in print, no raster memory, and it survives a server render — canvas needs an effect, a ref and a device-pixel-ratio dance. |