# QRCode

A theme-aware QR code with loading feedback and optional reveal animation.

Page: https://sui.draco.dev/docs/components/qr-code

### Example: qr-code-demo

```tsx
"use client";
import { Input } from "@workspace/ui/components/input";
import { Label } from "@workspace/ui/components/label";
import { QRCode } from "@workspace/ui/components/qr-code";
import { useId, useState } from "react";
import type { ExampleProps } from "../types";
export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  const id = useId();
  const [value, setValue] = useState("https://example.com/");
  return (
    <div className="grid w-full gap-4 sm:grid-cols-[1fr_auto]">
      <div className="grid content-start gap-2">
        <Label htmlFor={id}>{chinese ? "编码内容" : "Content to encode"}</Label>
        <Input
          id={id}
          value={value}
          onChange={(event) => setValue(event.target.value)}
        />
        <p className="text-muted-foreground text-sm">
          {chinese
            ? "编辑内容后二维码会更新。空值显示占位内容"
            : "Edit the content to regenerate the code. An empty value shows a placeholder."}
        </p>
      </div>
      <QRCode
        value={value}
        size={180}
        label={
          chinese ? "当前输入内容的二维码" : "QR code for the current input"
        }
      />
    </div>
  );
}
```

## Installation

```bash
bunx --bun shadcn@latest add @sui/qr-code
```

Install with the shadcn CLI or use the shared `@workspace/ui` package. Follow the [installation guide](/docs/installation) to configure the registry, load styles, and choose import aliases.

## Usage

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

<QRCode value="https://example.com/" label="Example website QR code" size={220} />;
```

## Loading and empty states

Generation loads on demand in the client. The server renders a stable loading placeholder. Changing `value` generates a fresh code and discards the previous request result. `loading` keeps the placeholder visible even if generation has completed. An absent or empty value keeps the loading placeholder rather than generating an invalid code.

By default, the code uses the neutral `--foreground` color on a solid `--card` surface. It follows light and dark modes without inheriting the accent color. Theme changes recolor the existing code without generating it again. The basic code appears as soon as its image loads. Loading and empty states use independently pulsing dots with a roughly 800ms blur entrance and no visible text. Generation failures show an error icon. The loading dots, error icon, and logo surface follow the same neutral theme. The logo appears only when the code is ready.

### Example: qr-code-states

```tsx
"use client";
import { QRCode } from "@workspace/ui/components/qr-code";
import type { ExampleProps } from "../types";
export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  return (
    <div className="grid w-full gap-4 sm:grid-cols-3">
      <div className="grid content-start justify-items-center gap-3">
        <span className="text-sm">
          {chinese ? "基础二维码" : "Basic QR code"}
        </span>
        <QRCode
          value="SUI"
          size={144}
          label={chinese ? "包含 SUI 文字的二维码" : "QR code containing SUI"}
        />
      </div>
      <div className="grid content-start justify-items-center gap-3">
        <span className="text-sm">{chinese ? "加载中" : "Loading"}</span>
        <QRCode value="SUI" loading size={144} />
      </div>
      <div className="grid content-start justify-items-center gap-3">
        <span className="text-sm">{chinese ? "尚无内容" : "No content"}</span>
        <QRCode
          size={144}
          label={chinese ? "尚无二维码内容" : "No QR code content yet"}
        />
      </div>
    </div>
  );
}
```

## Optional animation

Enable `animated` for a roughly 750ms reveal. Both modes use the dot-matrix skeleton while loading. Reduced motion freezes the dots and skips the reveal. The basic version displays the result directly.

### Example: qr-code-animated

```tsx
"use client";
import { QRCode } from "@workspace/ui/components/qr-code";
import type { ExampleProps } from "../types";

export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  return (
    <QRCode
      value="https://example.com/"
      animated
      size={220}
      label={chinese ? "动画二维码" : "Animated QR code"}
    />
  );
}
```

## Margin and finder patterns

`margin` controls the quiet space around the code in CSS pixels at the requested `size`, defaulting to `16`. Set it to `0` to supply your own solid-color quiet zone. It scales with the code in smaller containers. Negative values become zero, excessive values are limited to `size / 2 - 4`, and non-finite values use the default.

Finder patterns use concentric rounded outer rings and rounded solid centers. Their outer edge, inner edge, and center have corner radii of `2.5`, `1.5`, and `0.5` modules, preserving the same corner centers as each edge moves inward. Insufficient quiet space, small sizes, and busy backgrounds reduce scan reliability. Verify scanning at the actual display size.

### Example: qr-code-margin

```tsx
"use client";

import { QRCode } from "@workspace/ui/components/qr-code";
import type { ExampleProps } from "../types";

export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  return (
    <div className="grid w-full gap-6 sm:grid-cols-3">
      {[0, 16, 24].map((margin) => (
        <div key={margin} className="grid justify-items-center gap-3">
          <QRCode
            value="https://example.com/"
            size={160}
            margin={margin}
            label={chinese ? "示例链接二维码" : "Example link QR code"}
          />
          <span className="text-muted-foreground text-sm">margin={margin}</span>
        </div>
      ))}
    </div>
  );
}
```

## Logo and scan reliability

`logo` places a small React node over the center. The generator uses high error correction and retains the surrounding quiet zone. Keep the logo small, preserve the quiet zone, and verify scanning at the size and background where the code will be used. A logo can still reduce scan reliability even with error correction.

`QRCode` renders only the code. Compose your own layout or caption when needed. The code stays sharp while its neutral foreground and default solid background follow the theme. Test both light and dark modes with the scanners your application supports.

### Example: qr-code-logo

```tsx
"use client";
import { QRCode } from "@workspace/ui/components/qr-code";
import type { ExampleProps } from "../types";
export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  return (
    <QRCode
      value="https://example.com/"
      size={220}
      label={chinese ? "示例链接二维码" : "Example link QR code"}
      logo={
        <span
          className="grid size-7 place-items-center rounded-md bg-black text-white text-xs"
          aria-hidden="true"
        >
          S
        </span>
      }
    />
  );
}
```

## Glass mode

Enable `glass` to use the shared glass material across the entire background, and configure its material and rendering mode with `GlassProvider`. The code fills the surface without a separate rim or solid inner panel. Its `--foreground` modules remain sharp SVG content over the transparent code layer; the small logo surface remains solid. Switching the theme or glass material preserves the encoded content. `glassMaterial` overrides the provider material for this instance.

The background behind the glass changes the visible contrast and quiet zone. Test scanning on the actual background, in both appearance modes and at the final display size. Glass does not guarantee the contrast of the default solid surface.

### Example: qr-code-glass

```tsx
"use client";

import { GlassProvider } from "@workspace/ui/components/glass";
import { QRCode } from "@workspace/ui/components/qr-code";
import type { ExampleProps } from "../types";

export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  const defaultLabel = chinese ? "默认" : "Default";
  return (
    <GlassProvider mode="css" material="clear">
      <div className="grid w-full gap-6 rounded-2xl bg-[linear-gradient(135deg,var(--primary),var(--background)_45%,var(--primary))] p-8 sm:grid-cols-2">
        {[false, true].map((glass) => (
          <div key={String(glass)} className="grid justify-items-center gap-3">
            <QRCode
              value="https://example.com/"
              size={200}
              glass={glass}
              label={chinese ? "示例链接二维码" : "Example link QR code"}
            />
            <span className="text-sm">{glass ? "glass" : defaultLabel}</span>
          </div>
        ))}
      </div>
    </GlassProvider>
  );
}
```

## API reference

| Prop | Type | Default / behavior |
| --- | --- | --- |
| `value` | `string` | Content to encode; empty keeps placeholder. |
| `loading` | `boolean` | `false`. |
| `animated` | `boolean` | `false`; optional reveal animation. |
| `size` | `number` | `256`; requested width, bounded to 64–1024 pixels and constrained by the container. |
| `label` | `string` | `"QR code"`; accessible description. |
| `margin` | `number` | `16`; quiet space in CSS pixels at the requested size. |
| `glass` | `boolean` | Off by default; uses glass across the code background. |
| `glassMaterial` | `"clear" \| "frosted"` | Overrides the provider material. |
| `logo` | `ReactNode` | Optional center overlay. |
| `render`, `ref`, other props | Div / useRender props | Forwarded to the root. |

The module exports `QRCode`, `QRCodeProps`, and `qrCodeVariants`. Generation uses [qr-code-styling](https://github.com/kozakdenys/qr-code-styling).
