QR Code
A flexible QR code generator with square/dotted module shapes, solid or gradient fills, and a center logo overlay slot — the same building blocks as Telegram's QR share sheet.
Last updated August 16, 2026
Overview
Installation
$ npx shadcn@latest add https://ui.persian-labs.ir/r/qr-code.jsonUsage
import {
QrCode,
QrCodeDownload,
QrCodeFrame,
} from "@/components/ui/qr-code"
export function Example() {
return (
<QrCode value="https://ui.persian-labs.ir">
<QrCodeFrame />
<QrCodeDownload fileName="qr-code.png" mimeType="image/png">
Download
</QrCodeDownload>
</QrCode>
)
}Variant
The encoded matrix is traced into a single SVG <path>, one dark module at a time. The dotted variant is built by rendering that path invisibly inside an SVG <mask>, which turns it into a reusable stencil — the actual visible fill (a tiled dot pattern here) is painted through the mask, so it's clipped to exactly the QR module shape without needing per-cell coordinates.
Gradient Color
The same masking technique lets color accept a gradient config instead of a solid CSS color, rendered as an SVG <linearGradient> / <radialGradient> painted through the same mask. Keep enough contrast between the pattern and the frame's white background — a low-contrast gradient (e.g. light-on-light) makes the code unreliable or impossible to scan.
Center Logo
QrCodeOverlay renders any content — an image, an icon, a brand mark — centered over the code, independent of the variant/color system above. It's aria-hidden by default since the QR code itself carries the meaning, not the logo.
Loading Skeleton
QrCodeSkeleton matches QrCodeFrame's default footprint exactly, so swapping between the two while the encoded value loads causes zero layout shift.
Download
QrCodeDownload exports the frame as an image client-side — the SVG frame is serialized, drawn onto an offscreen canvas, and saved as a PNG/JPEG/ WebP through a temporary download link. It renders as a styled button by default — pass an icon and visible text as children (as in the examples above), or an aria-label if you render it icon-only.
RTL
QR data itself is direction-agnostic visual data, so the code renders identically regardless of the surrounding document direction — the labels and download button around it are what need to follow the page's direction.