Render a scannable QR code from any string — a URL, an otpauth:// MFA
enrollment URI, a Wi-Fi credential, and so on. The value is encoded with the
dependency-free uqr library.
Compose a QrCode.Frame (the svg) wrapping a QrCode.Pattern (the modules)
inside a QrCode.Root, and optionally drop a QrCode.Overlay in the center for
a brand logo.
1import { QrCode } from "@ngrok/mantle/qr-code";2import { NgrokLettermarkIcon } from "@ngrok/mantle/icons";3 4<QrCode.Root5 value="otpauth://totp/ngrok:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=ngrok"6 ecc="H"7>8 <QrCode.Frame>9 <QrCode.Pattern />10 </QrCode.Frame>11 <QrCode.Overlay>12 <NgrokLettermarkIcon className="size-7" />13 </QrCode.Overlay>14</QrCode.Root>;The tile is intentionally white with dark modules in every theme — inverting a QR code's colors makes it unreliable for many scanners, so it stays black-on-white by default for scannability.
The minimal QR code is a Root with a Frame and Pattern. The default error
correction level is L (~7% recovery), which is plenty when there is no overlay
covering modules.
1import { QrCode } from "@ngrok/mantle/qr-code";2 3<QrCode.Root value="https://ngrok.com">4 <QrCode.Frame>5 <QrCode.Pattern />6 </QrCode.Frame>7</QrCode.Root>;A QrCode.Overlay hides the modules beneath it, so raise the error correction
level via the ecc prop to keep the code scannable. Levels are L (~7%), M
(~15%), Q (~25%), and H (~30%). Prefer H whenever a logo is present.
1<QrCode.Root value="https://ngrok.com" ecc="H">2 <QrCode.Frame>3 <QrCode.Pattern />4 </QrCode.Frame>5 <QrCode.Overlay>6 <NgrokLettermarkIcon className="size-7" />7 </QrCode.Overlay>8</QrCode.Root>The Frame defaults to a square size-48. Because the encoded pattern scales
to the frame's viewBox, resize the whole code by passing a different size to
the Frame's className.
1<QrCode.Root value="https://ngrok.com">2 <QrCode.Frame className="size-32">3 <QrCode.Pattern />4 </QrCode.Frame>5</QrCode.Root>A QR code includes a margin of light modules around the pattern — the quiet
zone — that scanners rely on to locate the code. It's encoded into the grid
itself, not cosmetic CSS padding, so adjust it with the quietZone prop on
Root rather than className. It defaults to 4, the QR spec's recommended
margin. Lower it to tighten the surrounding whitespace; avoid 0, which makes
the code unreliable for many scanners.
1<QrCode.Root value="https://ngrok.com" quietZone={1}>2 <QrCode.Frame>3 <QrCode.Pattern />4 </QrCode.Frame>5</QrCode.Root>Compose the parts of a QrCode together to build your own:
QrCode.Root├── QrCode.Frame│ └── QrCode.Pattern└── QrCode.OverlayQrCode.Root and QrCode.Overlay support the asChild prop. When asChild is
true, the part renders its single child instead of its default element,
forwarding all class names, data-* attributes, and behavior onto that child —
for example, to render the whole code as a link.
QrCode.Frame and QrCode.Pattern render fixed SVG elements (svg and path)
and do not support asChild — the QR pattern must live inside an svg, so those
elements cannot be swapped.
1<QrCode.Root asChild value="https://ngrok.com">2 <a href="https://ngrok.com">3 <QrCode.Frame>4 <QrCode.Pattern />5 </QrCode.Frame>6 </a>7</QrCode.Root>The root container. Encodes value and renders a white tile around the code.
Renders a div.
All props from div, plus:
The svg frame that holds the Pattern. Defaults to size-48 and scales the
pattern to fit via its viewBox. Renders an svg. Does not support asChild.
Accepts all props from svg.
The encoded modules, rendered as a single path inside the Frame. Always
filled black. Renders a path. Does not support asChild.
Accepts all props from path.
A container centered on top of the code, typically holding a brand logo. Renders
a white rounded "punch-out" so the logo stays legible. Renders a div.
All props from div, plus: