Qr Code
Vue data-displayStatic QR code renderer — pass `value` and a size, get an SVG. Useful for sign-in links, share URLs, and Wi-Fi credentials. No dependencies on a heavy QR library.
Also available for React ->Installation
$ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/qr-code.json$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/qr-code.json$ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/qr-code.json$ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/qr-code.json
Or with the named registry:
npx shadcn-vue@latest add @uipkge/qr-code
Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
value | string | — | required |
type | QRCodeType | 'canvas' | optional |
size | number | 160 | optional |
color | string | '#000000' | optional |
bgColor | string | '#ffffff' | optional |
icon | string | — | optional |
iconSize | number | { width: number; height: number } | — | optional |
errorLevel | QRCodeErrorLevel | 'M' | optional |
bordered | boolean | true | optional |
status | QRCodeStatus | 'active' | optional |
marginSize | number | 0 | optional |
class | HTMLAttributes['class'] | — | optional |
Dependencies
Files (2)
-
app/components/ui/qr-code/QRCode.vue 5.3 kB
<script setup lang="ts"> import type { HTMLAttributes } from 'vue' import { computed, ref, watch } from 'vue' // @ts-expect-error -- qrcode ships no type declarations; consumers may install @types/qrcode optionally import QRCodeLib from 'qrcode' import { Loader2, RotateCcw, Check, ScanLine } from 'lucide-vue-next' import { cn } from '@/lib/utils' export type QRCodeType = 'canvas' | 'svg' export type QRCodeStatus = 'active' | 'expired' | 'loading' | 'scanned' export type QRCodeErrorLevel = 'L' | 'M' | 'Q' | 'H' const props = withDefaults( defineProps<{ value: string type?: QRCodeType size?: number color?: string bgColor?: string icon?: string iconSize?: number | { width: number; height: number } errorLevel?: QRCodeErrorLevel bordered?: boolean status?: QRCodeStatus marginSize?: number class?: HTMLAttributes['class'] }>(), { type: 'canvas', size: 160, color: '#000000', bgColor: '#ffffff', errorLevel: 'M', bordered: true, status: 'active', marginSize: 0, }, ) const emit = defineEmits<{ refresh: [] }>() const qrDataUrl = ref('') const qrSvg = ref('') const isGenerating = ref(false) const iconDimensions = computed(() => { if (typeof props.iconSize === 'number') { return { width: props.iconSize, height: props.iconSize } } return props.iconSize ?? { width: 40, height: 40 } }) const errorCorrectionLevel = computed(() => props.errorLevel) async function generateQR() { if (!props.value || props.status === 'loading') return isGenerating.value = true try { const options = { width: props.size, margin: props.marginSize, color: { dark: props.color, light: props.bgColor, }, errorCorrectionLevel: errorCorrectionLevel.value, } if (props.type === 'svg') { qrSvg.value = await QRCodeLib.toString(props.value, { type: 'svg', ...options, }) } else { qrDataUrl.value = await QRCodeLib.toDataURL(props.value, options) } } catch (e) { console.error('QR Code generation failed:', e) } finally { isGenerating.value = false } } watch( () => [ props.value, props.type, props.size, props.color, props.bgColor, props.errorLevel, props.marginSize, props.status, ], () => generateQR(), { immediate: true }, ) function downloadQR() { const link = document.createElement('a') link.download = `qrcode-${props.value.slice(0, 20)}.png` link.href = qrDataUrl.value link.click() } function handleRefresh() { emit('refresh') } const statusOverlay = computed(() => { switch (props.status) { case 'expired': return { icon: RotateCcw, text: 'Expired', action: handleRefresh, } case 'scanned': return { icon: Check, text: 'Scanned', action: null, } case 'loading': return { icon: Loader2, text: 'Loading...', action: null, } default: return null } }) </script> <template> <div data-uipkge data-slot="qr-code" :class=" cn('inline-flex flex-col items-center gap-2', bordered && 'bg-background rounded-lg border p-4', props.class) " > <div class="relative inline-flex items-center justify-center overflow-hidden" :style="{ width: `${size}px`, height: `${size}px` }" > <!-- QR Code --> <template v-if="type === 'svg' && qrSvg"> <div v-html="qrSvg" class="size-full" /> </template> <template v-else-if="qrDataUrl"> <img :src="qrDataUrl" :alt="`QR Code for ${value}`" class="size-full" /> </template> <!-- Icon overlay --> <div v-if="icon && status === 'active'" class="absolute inset-0 flex items-center justify-center"> <div class="overflow-hidden rounded-md bg-white shadow-sm" :style="{ width: `${iconDimensions.width}px`, height: `${iconDimensions.height}px`, }" > <img :src="icon" alt="" class="size-full object-cover" /> </div> </div> <!-- Status overlay --> <div v-if="statusOverlay" class="absolute inset-0 flex flex-col items-center justify-center gap-2 bg-white/90 backdrop-blur-sm" > <component :is="statusOverlay.icon" class="size-8" :class="status === 'loading' && 'animate-spin'" /> <span class="text-foreground text-sm font-medium">{{ statusOverlay.text }}</span> <button v-if="statusOverlay.action" type="button" class="bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring inline-flex items-center gap-1 rounded-md px-3 py-1 text-xs font-medium focus-visible:ring-2 focus-visible:outline-none" @click="statusOverlay.action" > <ScanLine class="size-3" /> Refresh </button> </div> </div> <!-- Download button --> <slot name="extra"> <button v-if="type === 'canvas' && status === 'active' && qrDataUrl" type="button" class="text-muted-foreground hover:text-foreground focus-visible:ring-ring text-xs underline-offset-2 hover:underline focus-visible:ring-2 focus-visible:outline-none" @click="downloadQR" > Download </button> </slot> </div> </template> -
app/components/ui/qr-code/index.ts 0.1 kB
export { default as QRCode } from './QRCode.vue' export type { QRCodeType, QRCodeStatus, QRCodeErrorLevel } from './QRCode.vue'
Raw manifest: https://uipkge.dev/r/vue/qr-code.json