Skip to content

QRCode — spec

Citing Ant Design QRCode, MUI (no built-in), shadcn-ui (no built-in)

Purpose

Renders a QR code from a string (URL, payment ID, contact info). Used in Korean fintech (계좌이체 QR, KakaoPay QR), authentication flows, content sharing.

Anatomy

┌─────────────────────┐
│ ▣ ▢ ▣ ▢ ▢ ▣ ▢ ▣    │
│ ▢ ▢ ▣ ▣ ▢ ▢ ▣ ▢    │
│ ▣ ▢ ▢ ▣ ▣ ▢ ▢ ▣    │  ← QR matrix
│ ▢ ▣ ▣ ▢ ▢ ▣ ▣ ▢    │
│ ▣ ▢ ▣ ▢ ▣ ▢ ▣ ▢    │
└─────────────────────┘
       [logo]                ← optional center logo

API

<QRCode
  value="https://app.example.com/pay/abc123"
  size={200}
  errorLevel="M"
  iconSrc="/logo.png"
  bgColor="#ffffff"
  fgColor="#000000"
/>
Prop Type Default Description
value string URL or text to encode
size number 160 Pixel size (square)
errorLevel "L" \| "M" \| "Q" \| "H" "M" Error correction; H tolerates more damage / supports center logo better
bgColor string "#FFFFFF"
fgColor string "#000000"
iconSrc string Center logo URL
iconSize number 40
padding number 16 Quiet zone around code
loading boolean false
errorState boolean false Show error placeholder

Error correction levels

QR codes can recover from damage. Higher levels tolerate more but require more dense matrix:

Level Recovery Use
L 7% Print, small QRs without logos
M (default) 15% Most cases
Q 25% Outdoor / damaged contexts
H 30% Required if you put a center logo (logo replaces QR data)

Sizes

Use Size (px)
Inline (in a card) 120–160
Standard 200–240
Modal / dedicated screen 280–320
Full-screen sharing 400+

Always render at integer pixel sizes (no fractional) — fractional pixels blur.

Color customization

  • High contrast required: fg and bg need ~7:1+ contrast for scanning reliability.
  • Brand colors: tempting but risky. Black-on-white is most reliable. If using brand color: dark navy or deep purple work; avoid yellows / pastels.
  • Inverted (light fg on dark bg): scannable but less reliable on older devices.

Adding a center logo (brand mark) replaces some of the QR data. Requires error level H (30% recovery) for reliable scanning.

<QRCode
  value="..."
  errorLevel="H"
  iconSrc="/logo.png"
  iconSize={size * 0.2}        // ~20% of total size
/>

Don't make the logo too large — exceeds QR's recovery capacity, fails to scan.

States

State Visual
Default QR rendered
Loading Skeleton or spinner
Error (failed to encode) Placeholder + retry
Refreshing Brief fade (for time-limited QRs)

For time-limited QRs (e.g., KakaoPay session-based codes): show a countdown OR auto-refresh before expiration.

Tokens consumed

--color-bg-default            (default QR bg)
--color-text-primary          (default QR fg)
--color-bg-subtle             (loading skeleton)
--color-error                  (error state)
--space-md
--radius-md                    (around the QR container, not the QR itself)

Accessibility

  • Wrap in a labeled container: <div role="img" aria-label="QR 코드: payment session abc123">.
  • Provide a text alternative as a fallback:
    <QRCode value={url} />
    <p>또는 직접 방문: <a href={url}>{url}</a></p>
    
  • For session-based QRs (KakaoPay, banking): provide an alternative input method (manual code entry).

Korean fintech use cases

  • 계좌이체 QR: 송금자가 받는 계좌의 QR을 스캔 → 계좌번호 자동 입력
  • 결제 QR: 사장님 화면에 QR → 손님이 KakaoPay/NaverPay로 스캔 → 결제
  • 인증 QR: 본인인증 / 로그인 — 모바일 앱이 QR을 보여주고 데스크톱이 스캔 (또는 반대)
  • 공유 QR: 가게 정보, 친구 추천 코드 등

For 결제 QR: the QR usually encodes a session token + payment URL, refreshing every ~60s for security. Implement with key={refreshKey} to force re-render on refresh.

Code example

// Static URL
<QRCode value="https://example.com/me" size={200} />

// Payment QR with logo and time limit
function PaymentQRScreen({ session }) {
  const [refreshKey, setRefreshKey] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      session.refresh();
      setRefreshKey(k => k + 1);
    }, 60_000);
    return () => clearInterval(timer);
  }, [session]);

  return (
    <div className="flex flex-col items-center">
      <QRCode
        key={refreshKey}
        value={session.paymentUrl}
        size={280}
        errorLevel="H"
        iconSrc="/brand.png"
        iconSize={56}
      />
      <p className="text-sm text-text-secondary">
        QR이 자동 갱신됩니다 · 60  갱신
      </p>
      <p className="mt-2">
        결제 금액: <strong>{amount.toLocaleString()}</strong>
      </p>
    </div>
  );
}

Edge cases

  • Very long URL encoded: QR matrix becomes dense. Cap to ~500 chars; for longer, shorten via your own URL shortener service.
  • No internet at scan time: pre-encode locally; don't rely on online encoding service.
  • Color-blind users: don't use red/green for QR — black or dark navy on white.
  • Print at small size: use error level Q or H and minimum 25mm physical size.
  • Encoding errors: if value is unencodable (rare), show fallback with the text URL plain.

Don't

  • Don't restyle a QR with very low contrast — defeats the purpose.
  • Don't put logo without error level H.
  • Don't omit text alternative — assistive tech can't read QRs.
  • Don't make the user wait > 1s for QR to render. Pre-compute when possible.
  • Don't use animated QR (rotating, color-changing). It's a static identifier — animation breaks scans.

References

  • Ant Design: refs/ant-design/components/qr-code/QRCode with all options.
  • MUI: no built-in. Use libraries like qrcode.react or react-qr-code.
  • shadcn-ui: no built-in. Use libraries.

Common libraries: - qrcode (Node-style, can run in browser) - qrcode.react (React component) - react-qr-code (lightweight)

Cross-reference