# React QR Code with Logo: Rounded Modules, PNG and SVG

> Build a React QR code with logo that still scans: rounded or dotted modules, a cleared center tile, the right error correction level and PNG or SVG export.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Oct 19, 2026
- Category: [Component guides](https://wingo-ui.com/blog/category/components)
- Reading time: 9 min
- Canonical: https://wingo-ui.com/blog/react-qr-code-with-logo

## TL;DR

A React QR code with logo scans reliably when the logo sits on a cleared center tile, covers a small part of the code and the error correction is raised to Q or H to pay for the lost modules. Keep dark modules on a light tile in both themes and a quiet zone around the code. The Wingo UI QR Code does all of that from one value prop, draws rounded or dotted modules, and exports PNG or SVG with the logo embedded.

You need a React QR code with logo for an invoice, a ticket or the office Wi-Fi card. The first version is a black and white grid from a library. Then design asks for the brand mark in the middle, rounded dots and the brand orange on the corner eyes, marketing asks for a 2048px PNG for print, and somebody points a phone at the dark mode screen and nothing happens. This guide covers what keeps a styled code scannable, the props that matter in the Wingo UI [QR Code](https://wingo-ui.com/components/qr-code), how PNG and SVG export works, and a full example.

## Can a QR code with a logo in the middle still scan?

Yes, because QR codes carry error correction. Redundant data spread across the grid lets a scanner rebuild the message when part of it is missing, and a logo is damage you add on purpose. Four levels set how much redundancy the code carries:

| Level | Recovers about | Cost |
|---|---|---|
| L | 7% | smallest grid |
| M | 15% | the common default |
| Q | 25% | denser grid |
| H | 30% | densest grid |

The trade-off is density: the same url needs more modules per side at H than at L, so each module is smaller and harder to read from a distance. That is why a good React QR code generator with logo does two things together: it clears only the modules the logo actually covers, and it raises the level just enough to pay for them.

The Wingo UI QR Code raises the level for you. With a logo, `level` becomes at least `Q`, and `H` when `logoSize` is above 0.2. The level in use is on the root as `data-level`, and the `effectiveQrLevel(level, logoSize)` helper returns the same answer if you want to show it. The default `logoSize` is 0.22 of the code width, so the logo tile covers about 5% of the grid (0.22 squared). The cleared square adds a ring of air around it, and the total is still well under the 30% that H can recover. Because 0.22 is above 0.2, the default logo runs at H.

Three rules decide whether the code scans:

1. **Leave the three corner eyes alone.** The finder patterns are how a camera finds and orients the code. Recolor them, round them, never cover them.
2. **Dark modules on a light background.** Not every scanner reads inverted (light on dark) codes. The component keeps `#111111` on `#ffffff` in dark mode on purpose.
3. **A quiet zone.** An empty border around the code separates it from the page. The default `margin` is 2 modules, drawn inside a tile that adds 12px of padding in the same color. For a bare code on paper with `framed={false}`, set `margin={4}`, the ISO minimum.

## What do the React QR code libraries offer?

Three packages come up for a React QR code with a logo, each with a different shape. Facts are from each project's README and its latest npm release as of October 19, 2026.

| Option | Output | Logo | Styling | Export |
|---|---|---|---|---|
| [qrcode.react](https://github.com/zpao/qrcode.react) (ISC, 4.2.0) | `QRCodeSVG` or `QRCodeCanvas` | `imageSettings` with `src`, `width`, `height` and `excavate` | colors, level (default L), `marginSize` (default 0) | not built in |
| [react-qrcode-logo](https://github.com/gcoro/react-qrcode-logo) (MIT, 4.1.0) | canvas | `logoImage`, `logoWidth` (default size × 0.2), `removeQrCodeBehindLogo` (default false) | `qrStyle` squares, dots or fluid, `eyeRadius`, `ecLevel` (default M) | `download()` as png, jpg or webp, plus `generateBlob` without mounting |
| [qr-code-styling](https://github.com/kozakdenys/qr-code-styling) (MIT, 1.9.2) | canvas or svg, framework agnostic | `image` with `imageSize` (default 0.4) and `hideBackgroundDots` (default true) | six dot types, corner square and corner dot types, gradients | `download()` as png, jpeg, webp or svg |
| [Wingo UI QR Code](https://wingo-ui.com/components/qr-code) (Pro) | inline SVG, source in your repo | `logo` url or node, `logoSize` 0.1 to 0.3, `excavate` (default true) | `moduleStyle`, `finderStyle`, `finderColor` | PNG, SVG, share, copy image |

qrcode.react is the right pick when you want a plain, small, well-known code and will write your own download button. react-qrcode-logo is the better choice when you want a canvas, a raster download and blob helpers that run without a mounted component. qr-code-styling has the widest range of React QR code styling options (classy and extra-rounded shapes, gradients) if you are happy to wrap a vanilla library in a ref. Watch the defaults in both: qrcode.react starts at level L with no margin, and react-qrcode-logo keeps the modules behind the logo unless you turn on `removeQrCodeBehindLogo`, so a logo there needs a higher `ecLevel`. The rest of this guide covers the Wingo UI component.

## What props does a React QR code with a logo need?

One. `value` is the text to encode: a url, a `WIFI:T:WPA;S:...;;` string or a payment string. Everything else has a default that scans: rounded modules, rounded eyes, 192px, level M, dark on white.

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

```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"
    />
  )
}
```

The props you will change most:

- **`logo`**: an image url or a React node, such as an inline `<svg>` mark. It is drawn on a cleared tile in the center with rounded corners in the `background` color.
- **`logoSize`**: the tile width as a fraction of the code, clamped to 0.1 to 0.3.
- **`excavate`**: clears the modules under the logo plus at least half a module of air around it (default true). Off lays the tile over the modules, which costs more error correction.
- **`size`**: the code width in px (the frame adds 12px of padding). It never grows past its container, and it is raised so every module is at least 1.5px, with a warning in development.
- **`caption`** and **`showValue`**: a line under the code, and the encoded text with a copy button and a [Tooltip](https://wingo-ui.com/components/tooltip).
- **`framed`** and **`radius`**: the tile with its shadow, and its corners from `none` to `full`.

An empty `value` shows an empty tile and text too long for the level an error tile. Nothing throws.

> Live demo (QR Code): Change the module and finder styles and the logo size in the playground: the level readout under the code shows when the logo raises it to Q or H. Try it at [QR Code](https://wingo-ui.com/components/qr-code) and install it with `npx wingo-ui@latest add qr-code`.

## How do I style a QR code in React without breaking it?

Change the shape of each cell, keep which cells are dark. `moduleStyle` has three values:

- `square`: classic crisp squares, drawn as horizontal runs so there are no seams.
- `rounded` (default): a corner rounds only where neither neighbor touches it, so neighboring modules merge into pills and blobs.
- `dots`: circles at 85% of the cell.

`finderStyle` shapes the three eyes as `square`, `rounded` or `circle`, an outer ring and an inner eye in the same shape. `finderColor` paints them in a second color, which is the safest place for a brand color: the eyes are large, so a mid-tone orange still reads.

```tsx
<QrCode
  value="https://acme.com/events/summit-2026"
  moduleStyle="dots"
  finderStyle="circle"
  color="#0B3D2E"
  finderColor="#D63F00"
  background="#F6F1E7"
  radius="lg"
  caption="Acme Summit 2026"
/>
```

Keep `color` dark and `background` light. A cream tile and a deep green works; light gray on white does not. `background="transparent"` is allowed for a code printed on a colored card, and then the contrast is up to you. If you build the palette with a picker, the [React color picker guide](https://wingo-ui.com/blog/react-color-picker) covers HEX and OKLCH input.

> Live demo (QR Code): Try the circle eyes with an orange finder color and the dots modules, then scan the screen with your phone camera. Try it at [QR Code](https://wingo-ui.com/components/qr-code) and install it with `npx wingo-ui@latest add qr-code`.

## How do I download a React QR code as PNG or SVG?

Turn on `downloadable`, which covers the usual React QR code download PNG request without a canvas helper of your own. A Download button opens a menu with a PNG at `exportSize` (1024px by default) and an SVG that stays sharp at any size. On phones the menu opens as a bottom sheet through the [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu), and where the device can share files a Share row opens the phone's share sheet. Where the clipboard accepts images, a Copy image button sits next to it. File names are timestamped: `fileName="Invoice 2026-0412"` downloads as `Invoice-2026-0412-2026-10-19-1640.png` (spaces become hyphens).

Exports are built from the same geometry the screen shows. The SVG export is standalone text with inline colors, and the PNG is that SVG drawn onto a canvas by the [file export helpers](https://wingo-ui.com/components/file-export). That has one consequence for logos: an exported file can only embed a logo the browser can read. Inline svg nodes and data urls always work, same-origin urls and urls with CORS headers are fetched once and inlined, and a cross-origin logo without CORS is left out of the export (the cleared tile and raised level stay). If your logo lives on a CDN, pass it as an inline svg or serve it with CORS.

For your own buttons, `setApi` hands you `{ toSvgString, toBlob, download, copy }` once after mount:

```tsx
"use client"

import { useState } from "react"
import { Download } from "lucide-react"
import { Button } from "@/components/ui/button"
import { QrCode, type QrCodeApi } from "@/components/ui/qr-code"
import { downloadBlob, timestampedFileName } from "@/lib/file-export"

export function PrintableCode() {
  const [api, setApi] = useState<QrCodeApi | null>(null)

  return (
    <div className="flex flex-col items-center gap-4">
      <QrCode value="https://acme.com/verify/2026-0412" logo="/logo.svg" setApi={setApi} />
      <Button
        variant="soft"
        leftIcon={<Download />}
        disabled={!api}
        onClick={async () => {
          if (!api) return
          const blob = await api.toBlob({ size: 2048 })
          downloadBlob(blob, timestampedFileName("Print 2048", "png"))
        }}
      >
        PNG for print
      </Button>
    </div>
  )
}
```

`toBlob` also takes `type`, `quality` and `background`, so a JPEG with a white background is one option away. Call `api.copy()` straight from a click handler: Safari only allows clipboard writes inside the user's gesture. `onExport` runs after every finished download, share or copy with the action, format, file name and blob, which is the place for analytics. To build the same SVG text outside a mounted component, `qrToSvgString(value, options)` takes the same options as the props and needs no DOM or canvas.

## Is the QR code accessible?

The code is an SVG with `role="img"` and an accessible name from `labels.image`, `QR code: <value>` by default, so a screen reader user hears where the code leads. Downloads and copies are announced in a polite live region. Tab reaches Download and Copy image in order, Enter or Space opens the menu, the arrow keys move through it and Escape returns focus to the trigger. Every visible and announced string lives in `labels` and merges over `DEFAULT_QR_CODE_LABELS`, so the component translates without a fork.

With `showValue` the url sits under the code with a copy button, for the person reading on the same phone the code would open. Copying uses the free [use-clipboard hook](https://wingo-ui.com/components/use-clipboard).

## What are the common QR code pitfalls?

- **A logo with no error correction to back it.** Covering modules at level L or M without raising it is the most common reason a branded code stops scanning. Here the level is raised for you; elsewhere set it yourself.
- **Too much text.** A long url with tracking parameters at level H becomes a dense grid of tiny modules. Shorten the url before you style the code.
- **Inverted colors in dark mode.** A light code on a dark page looks right and fails on scanners that do not handle inverted codes. Keep the light tile.
- **Not testing.** Scan the final export with two or three phones, from a screen and from paper, before it goes on anything printed.

## How do I add it to my project?

Run `npx wingo-ui@latest add qr-code`. The CLI copies `qr-code.tsx` into `components/ui`, adds the button, dropdown menu, skeleton and tooltip it imports, the clipboard hook in `hooks/`, the file export, motion and UI config helpers in `lib/`, and installs `qrcode`, `motion` and `lucide-react`. The QR Code is a Pro component ([pricing](https://wingo-ui.com/pricing)). The [React signature pad guide](https://wingo-ui.com/blog/react-signature-pad) uses the same PNG and SVG export helpers, and more single-component deep dives are in the [component guides category](https://wingo-ui.com/blog/category/components), next to the [React form components guide](https://wingo-ui.com/blog/react-form-components-guide).

## Components in this post

- [QR Code](https://wingo-ui.com/components/qr-code) (Pro): 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. Install: `npx wingo-ui@latest add qr-code`
- [File export](https://wingo-ui.com/components/file-export) (Pro): File export helpers for React apps: download blobs, text and JSON, turn a canvas or an SVG into a PNG, copy images and name files cleanly. Install: `npx wingo-ui@latest add file-export`
- [useClipboard](https://wingo-ui.com/components/use-clipboard) (Free): A React copy-to-clipboard hook with a copied flag that resets itself, and a fallback for pages without the Clipboard API. Install: `npx wingo-ui@latest add use-clipboard`

## FAQ

### How do I add a logo to a QR code in React?

Use a QR component that takes a logo and clears the modules under it, then raise the error correction level. In the Wingo UI QR Code you pass logo as an image url or an inline svg node, and the component clears a tile in the center and raises the level to at least Q.

### What error correction level should a QR code with a logo use?

Q or H. Q recovers about 25% of damaged data and H about 30%, so a logo that hides some modules still decodes. Higher levels make the grid denser, so keep the logo small rather than relying on H alone.

### How big can the logo in a QR code be?

Keep it well under the recovery limit and away from the three corner eyes. The Wingo UI QR Code clamps the logo tile to 10% to 30% of the code width and defaults to 22%, a tile that covers about 5% of the grid area.

### How do I download a React QR code as PNG?

Render the code as SVG, draw it onto a canvas at the size you want and save the canvas as a PNG blob. The Wingo UI QR Code does this with downloadable (a Download menu with PNG and SVG) or with setApi, which gives you toBlob, toSvgString, download and copy for your own buttons.

### Do styled QR codes with rounded dots still scan?

Yes, when the modules stay inside their grid cells, the finder patterns keep their shape and the contrast is high. Rounded and dotted modules only change the outline of each cell, so the same cells stay dark and the data is the same.

### Is the Wingo UI QR Code free?

No, the QR Code is a Pro component, and so are the file export helpers it uses. The button, tooltip, dropdown menu, skeleton and clipboard hook it builds on are free.

---

Source: https://wingo-ui.com/blog/react-qr-code-with-logo
