- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
"use client"
import {The QrCode component shows a scannable code on a light tile, with an optional center mark, a caller-controlled expired state and a copy-link fallback for people who can't scan. Use it for a pay-me link, a payment request for a set amount or a code that verifies a statement. When the other person can't scan, such as on the same phone, the copy link is the way in, so keep it next to the code.
Installation#
pnpm dlx shadcn@latest add @dots/qr-code
Usage#
import {
QrCode,
QrCodeCopyLink,
QrCodeImage,
} from "@/components/ui/qr-code"<QrCode value="https://example.com/pay/alex.morgan">
<QrCodeImage label="QR code to pay Alex Morgan" />
<QrCodeCopyLink label="Payment link" />
</QrCode>Composition#
Use the following composition to build a QR code:
QrCode
├── QrCodeImage
│ └── QrCodeOverlay
└── QrCodeCopyLinkFeatures#
- The tile stays light with dark modules in both themes, and its padding is the quiet zone
- An optional center mark, such as an avatar, that switches error correction to
Hso the code still reads - Error correction steps down on its own when the value is too long for the level, so long values never break the render
- An
expiredstate that fades the code, hides it from assistive tech and announces the change once - A copy-link fallback that copies the same value and goes inert while the code is expired
- Three sizes for a document, a screen and a share sheet
Mark#
Pass mark to QrCodeImage to put a small decorative mark over the center, such as the person's avatar. The mark is hidden from assistive tech, so the label still says what scanning does.
"use client"
import { EntityAvatar } from "@/components/ui/entity-avatar"Expired#
Set expired on QrCode when the code stops working, such as a payment request past its time. Put a QrCodeOverlay inside QrCodeImage to show what happened. The component runs no timers: you decide when a code expires and when to make a new one.
"use client"
import * as React from "react"Keep the control that makes a new code outside the overlay, so focus stays on it when the overlay goes away.
Sizes#
Use size on QrCodeImage to switch between sm for a printed document, default and lg for a share sheet.
"use client"
import { QrCode, QrCodeImage } from "@/components/ui/qr-code"Accessibility#
QrCodeImage renders the code as an SVG with role="img". Everything else is plain markup and the CopyField button.
Label the image#
label is required. Describe what scanning does, not what the image looks like.
<QrCodeImage label="QR code to pay Alex Morgan" />Announce expiry#
When expired turns on, a polite live region reads expiredLabel once, and the code is hidden from assistive tech. The overlay text can repeat the same words with aria-hidden="true" so they aren't read twice.
<QrCode value={value} expired expiredLabel="Code expired">
<QrCodeImage label="QR code to pay $25.00">
<QrCodeOverlay>
<span aria-hidden="true">Code expired</span>
</QrCodeOverlay>
</QrCodeImage>
</QrCode>Keep a way in#
People who can't scan, such as screen reader users on the same phone, need the value itself. Keep a QrCodeCopyLink next to the code. It names its copy button from label, such as "Copy payment link".
Contrast#
The tile stays light in dark mode on purpose: many scanners can't read inverted codes.
API Reference#
QrCode#
The root. It shares the value and the expired state with its parts. Accepts all div props.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | What the code encodes, usually a link. The copy link copies the same string. |
expired | boolean | false | The code no longer works. Fades the code and hides it from assistive tech. |
expiredLabel | string | "Code expired" | Announced once, politely, when the code expires. |
className | string | - | Additional classes to apply to the root element. |
QrCodeImage#
The code on a light tile. Accepts all div props except aria-label.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | What scanning does, such as "QR code to pay Alex Morgan". Required. |
mark | ReactNode | - | A small decorative mark over the center. Switches error correction to "H". |
level | "L" | "M" | "Q" | "H" | "H" with a mark, else "M" | Error correction. Steps down when the value is too long for the level. |
size | "sm" | "default" | "lg" | "default" | The tile size. |
className | string | - | Additional classes to apply to the tile. |
QrCodeOverlay#
Content over the code while it is expired. Accepts all div props.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the overlay. |
QrCodeCopyLink#
Copies the encoded value. Renders a CopyField and accepts its props except value, which comes from QrCode; label is optional here. Dimmed and inert while the code is expired. See Copy Field for the rest of the props.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "Link" | What the value is. Also names the copy button. |
className | string | - | Additional classes to apply to the copy field. |