# QR Code

> A React QR code generator with rounded or dotted modules, soft eyes, a center logo, any colors, a caption and one-tap PNG or SVG export.

- Type: React component (Media)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add qr-code`
- Docs: https://wingo-ui.com/components/qr-code

## Interaction

Point a phone camera at it: the code stays dark on a light tile in both themes, so it scans from a screen or a print. Changing the text crossfades to the new code; the first code springs in once (instantly with reduced motion). With downloadable on, Download opens a menu (a bottom sheet on phones) with a PNG at exportSize, an SVG and, where the device can share files, Share which opens the phone's share sheet; Copy image puts the PNG on the clipboard and turns into Copied for a moment. With showValue on, the encoded text sits under the caption with a copy button (a tooltip names it). Tab reaches the buttons in order, Enter or Space opens the menu, arrows move through it and Escape closes it back onto the trigger. Screen readers read the code as an image named after its text, and hear every copy and download.

## Installation

```bash
npx wingo-ui@latest add qr-code
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/qr-code` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/qr-code.tsx`

npm dependencies: `npm i qrcode motion lucide-react && npm i -D @types/qrcode`

Wingo UI dependencies (installed automatically):

- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) ([Markdown](https://wingo-ui.com/components/dropdown-menu.md))
- [Skeleton](https://wingo-ui.com/components/skeleton) ([Markdown](https://wingo-ui.com/components/skeleton.md))
- [Tooltip](https://wingo-ui.com/components/tooltip) ([Markdown](https://wingo-ui.com/components/tooltip.md))
- [useClipboard](https://wingo-ui.com/components/use-clipboard) ([Markdown](https://wingo-ui.com/components/use-clipboard.md))
- [File export](https://wingo-ui.com/components/file-export) ([Markdown](https://wingo-ui.com/components/file-export.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { QrCode } from "@/components/ui/qr-code"

export function PayInvoice() {
  return (
    <QrCode
      value="https://pay.acme.com/invoice/2026-0412?amount=3025.00"
      caption="Scan to pay $3,025.00"
      logo="/logo.svg"
      downloadable
      fileName="Invoice 2026-0412"
      labels={{ menuTitle: "Save the payment code" }}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | `"https://acme.com/i/2026-0412"` | The encoded text: a url, a Wi-Fi string (WIFI:T:WPA;S:Acme;P:...;;), a payment string. Any Unicode works (byte mode). An empty string shows the empty tile, text too long for the level shows the error tile; nothing throws. |
| `caption?` | `ReactNode` | `""` | A line under the code, like Scan to pay. On the tile it takes the module color; unframed or on a transparent tile it uses the page text color. |
| `showValue?` | `boolean` | `false` | Shows the encoded text under the caption in mono, truncated, with a copy button. |
| `logo?` | `ReactNode \| string` |  | An image url or a node (an inline svg mark, an <img>) centered on a cleared tile. It raises the error correction to Q, or H when logoSize is above 0.2. Exports embed url logos that can be fetched (same origin, CORS or data urls) and inline svg nodes. |
| `size?` | `number` | `192` | Width of the code in px (the frame adds 12px padding). It never grows past its container. Clamped so every module is at least 1.5px, with a warning in development. |
| `moduleStyle?` | `"square" \| "rounded" \| "dots"` | `"rounded"` | square: classic crisp squares. rounded: neighbouring modules merge into pills and blobs, corners round only where no neighbour touches. dots: circles at 85%. |
| `finderStyle?` | `"square" \| "rounded" \| "circle"` | `"rounded"` | The three corner eyes: an outer ring and an inner square, both in this shape. |
| `color?` | `string` | `"#111111"` | Module color. It stays dark in dark mode on purpose: scanners need dark modules on a light background. |
| `background?` | `string` | `"#ffffff"` | The tile and the quiet zone. "transparent" is allowed; the contrast is then up to you. |
| `finderColor?` | `string` | `""` | The eyes in a second color, like the brand orange. Empty uses the module color. |
| `framed?` | `boolean` | `true` | The tile: background, 12px padding and a raised shadow, so the code reads well on a dark page. Off draws the bare code. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corners of the tile: 0 / 12 / 16 / 24 / 32px. Unframed codes, the loading and empty tiles use a smaller matching step. |
| `logoSize?` | `number` | `0.22` | Width of the logo tile as a fraction of the code, clamped to 0.1 to 0.3. |
| `excavate?` | `boolean` | `true` | Clears the modules under the logo plus at least half a module around it. Off lays the logo tile over them. |
| `level?` | `"L" \| "M" \| "Q" \| "H"` | `"M"` | Error correction: L recovers 7% of damage, M 15%, Q 25%, H 30%. Higher levels mean denser codes. With a logo it is raised to at least Q (H when the logo is wider than 20%); data-level shows the level in use. |
| `margin?` | `number` | `2` | Quiet zone in modules, drawn in the background color inside the frame. 4 is the ISO minimum for print without a frame. |
| `downloadable?` | `boolean` | `false` | Shows the actions row: a Download menu (PNG, SVG, Share where files can be shared) and Copy image where the clipboard takes images. |
| `fileName?` | `string` | `"qr-code"` | Base name of exported files, passed to timestampedFileName: Invoice-2026-0412 becomes Invoice-2026-0412-2026-09-24-1640.png. |
| `exportSize?` | `number` | `1024` | Width and height of exported PNGs in px (the SVG export keeps the display size and scales freely). |
| `loading?` | `boolean` | `false` | A skeleton at the final size while the value is on its way; the code springs in when it arrives. |
| `animated?` | `boolean` | `true` | The entrance spring the first time a code appears. Value changes always crossfade; reduced motion turns both into a short fade. |
| `setApi?` | `(api: QrCodeApi) => void` |  | Receives { toSvgString(), toBlob({ size, type, quality, background }), download("png" \| "svg"), copy() } once after mount, for your own export buttons. Call copy() straight from a click handler (Safari). |
| `onExport?` | `(file: { action: "download" \| "share" \| "copy"; format: "png" \| "svg"; fileName: string; blob: Blob }) => void` |  | Called after every finished download, share or image copy, from the built-in actions and the api. The playground uses it for its last export readout. |
| `labels?` | `Partial<QrCodeLabels>` |  | Every visible and announced string: image(value), download, png, pngHint(size), svg, svgHint, share, copyImage, copied, copyFailed, copyValue, downloaded(fileName), exportFailed, menuTitle, cancel, tooLong, empty. English defaults in DEFAULT_QR_CODE_LABELS. |
| `classNames?` | `Partial<Record<"frame" \| "svg" \| "logo" \| "caption" \| "value" \| "actions", string>>` |  | Extra classes for the inner parts. |

## Parts

### qrToSvgString (function)

The same code as standalone SVG text without a DOM: for emails, PDFs and server routes. Options mirror the props (level, size, margin, moduleStyle, finderStyle, color, background, finderColor, logo as a url or svg markup, logoSize, excavate, title). Signature: (value: string, options?: QrSvgOptions) => string.

### getQrMatrix (function)

The raw module grid from the qrcode package, for custom renderers. Throws when the value does not fit. Signature: (value: string, level?: QrCodeLevel) => { size: number; get(row: number, col: number): boolean }.

### buildQrGeometry (function)

The path data the component draws (modules, the three finders, the logo box), in module units. Signature: (matrix: QrMatrix, options?: { margin; moduleStyle; finderStyle; logoSize: number | null; excavate }) => QrGeometry.

### effectiveQrLevel (function)

The level a logo raises the code to: at least Q, H when the logo is wider than 20%. Signature: (level: QrCodeLevel, logoSize: number | null) => QrCodeLevel.

### DEFAULT_QR_CODE_LABELS (constant)

The English strings for the image name, the export menu (PNG, SVG, share, copy image, copy text), the toasts and the empty and too long states.

### QrCodeApi (type)

The type of the object setApi receives: toSvgString, toBlob, download and copy.

## Examples

- **Module and finder styles**: square, rounded and dots modules with square, rounded and circle eyes.
- **Office Wi-Fi**: A WIFI: string joins the network from the camera app; the password stays copyable.
- **Custom colors**: Green modules with orange eyes on cream, and a transparent unframed code on a brand card.
- **Your own export buttons**: setApi hands you toBlob, toSvgString, download and copy.
- **Loading, then the code arrives**: A skeleton at the final size during a 1.4 s request, then the code springs in.
- **Error correction**: The same url at L, M, Q and H: more recovery, denser grid.
- **Empty, too long, loading**: No value, more text than a code holds, and a pending value. Nothing throws.

## See also

- [File export](https://wingo-ui.com/components/file-export) ([Markdown](https://wingo-ui.com/components/file-export.md))
- [useClipboard](https://wingo-ui.com/components/use-clipboard) ([Markdown](https://wingo-ui.com/components/use-clipboard.md))
- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))
- [Signature Pad](https://wingo-ui.com/components/signature-pad) ([Markdown](https://wingo-ui.com/components/signature-pad.md))
- [Image Cropper](https://wingo-ui.com/components/image-cropper) ([Markdown](https://wingo-ui.com/components/image-cropper.md))

---

Canonical: https://wingo-ui.com/components/qr-code
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
