# QrCode

> QR code drawn as SVG in the browser with dot, rounded or square modules, three eye styles, a centre logo slot, token or prop colours and SVG or PNG download, plus a full generator card.

Source: https://docs.nasaqui.com/components/qr-code

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/qr-code.json
```

A QR code that stays sharp at any size. The bit matrix comes from `uqr`; the drawing is Nasaq's own SVG,
so modules can be squares, dots or rounded, the three corner eyes have their own shape and colour, and a
logo can sit in the middle. `QrCodeGenerator` wraps it in a card with the controls.

## When to use

- Share a link, a Wi-Fi string, a vCard or a pairing payload.
- Let people make and download their own code (`QrCodeGenerator`).
- Any place you need the code as inline SVG, with no image request.

## When not to use

- One-dimensional retail or shipping codes: use [`Barcode`](https://docs.nasaqui.com/components/barcode).
- A TOTP set-up: [`TwoFactorSetup`](https://docs.nasaqui.com/components/two-factor-setup) already draws its own.
- A pairing flow with a countdown: [`WhatsappQrConnect`](https://docs.nasaqui.com/components/whatsapp-qr-connect).

## Import

```tsx
import { QrCode, QrCodeGenerator } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { QrCode } from "@fadymondy/nasaq/web";

export function Share({ url }: { url: string }) {
  return <QrCode value={url} downloadable downloadName="my-link" />;
}
```

## Anatomy

```
QrCode                          data-slot="qr-code"
├─ svg                          data-slot="qr-code-svg" (role="img", aria-label)
└─ actions (downloadable)       data-slot="qr-code-actions": Download SVG, Download PNG
QrCodeGenerator                 data-slot="qr-code-generator" (Card)
├─ content, module style, eye style, colours, logo upload
└─ preview: QrCode with downloads
```

## API

**QrCode**: every `div` prop except `children`, plus:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | required | What the code holds. |
| `moduleStyle` | `"square" \| "dots" \| "rounded"` | `"square"` | Shape of the data modules. |
| `eyeStyle` | `"square" \| "rounded" \| "circle"` | `"square"` | Shape of the three big corners. |
| `ecc` | `"L" \| "M" \| "Q" \| "H"` | `"M"` | Error correction. `"H"` is forced when there is a logo. |
| `margin` | `number` | `4` | Quiet zone in modules. |
| `fg`, `bg`, `eyeFg` | `string` | black, white, `fg` | Any CSS colour, including `var(--nq-...)` tokens. Keep strong contrast. |
| `logo` | `{ src: string; scale?: number }` | | Centre image. Scale is capped at 0.3. Modules under it are cleared. |
| `size` | `number \| "fill"` | `192` | Width and height in px. |
| `label` | `string` | "QR code for ..." | Accessible name. |
| `downloadable` | `boolean` | `false` | Show Download SVG and PNG. |
| `downloadName`, `pngSize` | `string`, `number` | `"qr-code"`, `1024` | File name and PNG width. |
| `labels` | `Partial<QrCodeLabels>` | | Override any string. |

**QrCodeGenerator**: `defaultValue`, `defaultModuleStyle`, `defaultEyeStyle`, `defaultFg`, `defaultBg`, `defaultLogo`, `downloadName`, `labels`.

**Helpers** (no React): `qrLayout(options)` returns `{ size, modules, eyes, logo }` path data; `qrSvgString(options)` returns a
standalone SVG string; `downloadSvg`, `downloadPng`, `svgToPng`, `resolveColor` are shared with `Barcode`.

## Examples

**Brand colours and a logo**

```tsx
import { QrCode } from "@fadymondy/nasaq/web";

export function Brand({ logo }: { logo: string }) {
  return <QrCode value="https://example.com" moduleStyle="rounded" eyeStyle="rounded" fg="var(--nq-brand)" logo={{ src: logo }} />;
}
```

**Make an SVG string on the server**

```tsx
import { qrSvgString } from "@fadymondy/nasaq/web";

export const svg = qrSvgString({ value: "https://example.com", fg: "rgb(0, 0, 0)", bg: "rgb(255, 255, 255)" });
```

## Accessibility

- The SVG is `role="img"` with an accessible name. The content is not read out: pass `label` when the value is long or private.
- Download buttons are real buttons with text. The colour inputs in the generator have labels.
- Contrast: dark on light with a quiet zone scans best. Light-on-dark and low-contrast pairs often fail in cameras.

## RTL & i18n

- The code is not mirrored: it is data, and scanners read it one way. The generator UI mirrors and has Arabic strings.
- The value field is `dir="ltr"`, because URLs and payloads are left-to-right.

## Styling & tokens

- Colours default to plain black and white for scanning. Tokens are resolved to concrete colours at download time, so files are portable.
- Target `[data-slot="qr-code"]`, `[data-slot="qr-code-svg"]`.

## Do / Don't

- Do test a styled code with a real phone before shipping it.
- Do keep a logo at or under 22 percent of the width.
- Don't use dots with a low error correction for long values: the code gets dense.
- Don't put a secret in a QR that will be shown to anyone else.

## Related

- [`Barcode`](https://docs.nasaqui.com/components/barcode)
- [`WhatsappQrConnect`](https://docs.nasaqui.com/components/whatsapp-qr-connect)
- [`TwoFactorSetup`](https://docs.nasaqui.com/components/two-factor-setup)

## Lab

https://docs.nasaqui.com/?path=/docs/components-utilities-qr-code--docs

## Code

### React

```tsx
import { QrCode } from "@fadymondy/nasaq/web";

export function Share({ url }: { url: string }) {
  return <QrCode value={url} downloadable downloadName="my-link" />;
}
```

### shadcn

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

export function Share({ url }: { url: string }) {
  return <QrCode value={url} downloadable downloadName="my-link" />;
}
```

### Vue

```vue
<script setup lang="ts">
import { NqQrCode } from "@fadymondy/nasaq/vue";

const url = "https://nasaq.fadymondy.com";
</script>

<template>
  <NqQrCode :value="url" downloadable download-name="my-link" />
</template>
```

### Blade

```blade
<x-nq::qr-code value="https://nasaq.fadymondy.com" downloadable download-name="my-link" />
```

### HTML + Alpine

```html
<div data-slot="qr-code" data-module-style="square" data-eye-style="square" dir="ltr"
    x-data="nqQrCode(JSON.parse('{\u0022value\u0022:\u0022https:\\\/\\\/nasaq.fadymondy.com\u0022,\u0022moduleStyle\u0022:\u0022square\u0022,\u0022eyeStyle\u0022:\u0022square\u0022,\u0022ecc\u0022:\u0022M\u0022,\u0022margin\u0022:4,\u0022fg\u0022:\u0022black\u0022,\u0022bg\u0022:\u0022white\u0022,\u0022eyeFg\u0022:null,\u0022logo\u0022:null,\u0022size\u0022:192,\u0022pngSize\u0022:1024,\u0022downloadName\u0022:\u0022my-link\u0022,\u0022label\u0022:null,\u0022labelFor\u0022:\u0022QR code for {value}\u0022}'))"
    x-modelable="value"
    class="inline-flex flex-col items-center gap-3">
    <svg data-slot="qr-code-svg" role="img"
     width="192" height="192"     x-bind:aria-label="name" x-bind:viewBox="viewBox" x-bind:shape-rendering="shape" x-bind:style="{ background: bg }"
    style="background: white"
    class="aspect-square rounded-control border border-border">
    <path x-bind:d="layout.modules" x-bind:fill="fg" />
    <g x-bind:fill="eyeFg ?? fg">
        <path x-bind:d="layout.eyes[0].ring" fill-rule="evenodd" />
        <path x-bind:d="layout.eyes[0].pupil" />
    </g>
    <g x-bind:fill="eyeFg ?? fg">
        <path x-bind:d="layout.eyes[1].ring" fill-rule="evenodd" />
        <path x-bind:d="layout.eyes[1].pupil" />
    </g>
    <g x-bind:fill="eyeFg ?? fg">
        <path x-bind:d="layout.eyes[2].ring" fill-rule="evenodd" />
        <path x-bind:d="layout.eyes[2].pupil" />
    </g>
    <image x-show="layout.logo" style="display: none" x-bind:href="layout.logo?.src" x-bind:x="layout.logo?.x" x-bind:y="layout.logo?.y"
        x-bind:width="layout.logo?.size" x-bind:height="layout.logo?.size" preserveAspectRatio="xMidYMid meet" />
</svg>
    <div class="flex flex-wrap items-center justify-center gap-2" dir="inherit" data-slot="qr-code-actions">
        <button data-slot="button"
     type="button"                         x-on:click="save(&#039;svg&#039;)" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M12 15V3"/>
  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
  <path d="m7 10 5 5 5-5"/>
</svg>Download SVG</button>
        <button data-slot="button"
     type="button"                         x-on:click="save(&#039;png&#039;)" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M12 15V3"/>
  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
  <path d="m7 10 5 5 5-5"/>
</svg>Download PNG</button>
    </div>
<p x-show="error" style="display: none" role="alert" class="text-caption text-nq-danger-text">Could not create the file. Try again.</p>
</div>
```
