No articles found
Try different keywords or browse our categories
Vue.js Scan & Generate QRCodes in Browser
Quick guide to generating and scanning QR codes in Vue.js browser applications. Simple examples with qrcode.vue and html5-qrcode.
Generate and scan QR codes directly in Vue.js applications. Works in the browser with no backend required.
Installation
npm install qrcode.vue3
npm install html5-qrcode
Generate QR Codes
Basic QR Code
<!-- QRGenerator.vue -->
<script setup>
import { ref } from 'vue'
import QrcodeVue from 'qrcode.vue3'
const qrValue = ref('https://example.com')
const qrSize = ref(256)
const qrLevel = ref('M')
</script>
<template>
<div class="qr-generator">
<h2>Generate QR Code</h2>
<input
v-model="qrValue"
type="text"
placeholder="Enter text or URL"
class="input-field"
/>
<div class="qr-display">
<qrcode-vue
:value="qrValue"
:size="qrSize"
:level="qrLevel"
/>
</div>
<button @click="downloadQR">Download QR Code</button>
</div>
</template>
<style scoped>
.qr-generator {
max-width: 500px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
.input-field {
width: 100%;
padding: 12px;
margin: 20px 0;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
}
.qr-display {
margin: 20px 0;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
button {
padding: 12px 24px;
background: #42b883;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #35a372;
}
</style>
Download QR Code
<script setup>
import { ref } from 'vue'
import QrcodeVue from 'qrcode.vue3'
const qrValue = ref('https://example.com')
const downloadQR = () => {
const canvas = document.querySelector('canvas')
if (canvas) {
const url = canvas.toDataURL('image/png')
const link = document.createElement('a')
link.download = 'qrcode.png'
link.href = url
link.click()
}
}
</script>
<template>
<div class="qr-generator">
<input v-model="qrValue" placeholder="Enter URL" />
<qrcode-vue
:value="qrValue"
:size="256"
level="H"
/>
<button @click="downloadQR">Download</button>
</div>
</template>
Scan QR Codes
Camera Scanner
<!-- QRScanner.vue -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { Html5QrcodeScanner } from 'html5-qrcode'
const scannedResult = ref('')
const isScanning = ref(false)
let scanner = null
const onScanSuccess = (decodedText) => {
scannedResult.value = decodedText
console.log('QR Code scanned:', decodedText)
// Stop scanning after success
if (scanner) {
scanner.clear()
isScanning.value = false
}
}
const onScanError = (error) => {
console.warn('Scan error:', error)
}
const initScanner = () => {
scanner = new Html5QrcodeScanner(
'qr-reader',
{
fps: 10,
qrbox: { width: 250, height: 250 }
},
false
)
scanner.render(onScanSuccess, onScanError)
isScanning.value = true
}
const restartScanner = () => {
scannedResult.value = ''
initScanner()
}
onMounted(() => {
initScanner()
})
onUnmounted(() => {
if (scanner) {
scanner.clear()
}
})
</script>
<template>
<div class="scanner-container">
<h2>Scan QR Code</h2>
<div id="qr-reader"></div>
<div v-if="scannedResult" class="result">
<h3>Scanned Result:</h3>
<p>{{ scannedResult }}</p>
<button @click="restartScanner">Scan Again</button>
</div>
</div>
</template>
<style scoped>
.scanner-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
#qr-reader {
border: 2px solid #ddd;
border-radius: 8px;
overflow: hidden;
margin: 20px 0;
}
.result {
margin-top: 20px;
padding: 20px;
background: #f0f9ff;
border-radius: 8px;
border: 2px solid #42b883;
}
.result h3 {
margin: 0 0 10px 0;
color: #42b883;
}
.result p {
word-break: break-all;
padding: 10px;
background: white;
border-radius: 4px;
margin: 10px 0;
}
button {
padding: 12px 24px;
background: #42b883;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #35a372;
}
</style>
Combined Component
<!-- QRManager.vue -->
<script setup>
import { ref } from 'vue'
import QRGenerator from './QRGenerator.vue'
import QRScanner from './QRScanner.vue'
const activeTab = ref('generate')
</script>
<template>
<div class="qr-manager">
<div class="tabs">
<button
:class="{ active: activeTab === 'generate' }"
@click="activeTab = 'generate'"
>
Generate QR Code
</button>
<button
:class="{ active: activeTab === 'scan' }"
@click="activeTab = 'scan'"
>
Scan QR Code
</button>
</div>
<QRGenerator v-if="activeTab === 'generate'" />
<QRScanner v-if="activeTab === 'scan'" />
</div>
</template>
<style scoped>
.qr-manager {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.tabs button {
flex: 1;
padding: 12px;
border: 2px solid #ddd;
background: white;
cursor: pointer;
border-radius: 8px;
font-size: 16px;
}
.tabs button.active {
background: #42b883;
color: white;
border-color: #42b883;
}
</style>
QR Code with Logo
<!-- QRWithLogo.vue -->
<script setup>
import { ref } from 'vue'
import QrcodeVue from 'qrcode.vue3'
const qrValue = ref('https://example.com')
const logoUrl = ref('/logo.png')
</script>
<template>
<div class="qr-logo-container">
<input
v-model="qrValue"
placeholder="Enter URL"
class="input-field"
/>
<div class="qr-wrapper">
<qrcode-vue
:value="qrValue"
:size="300"
level="H"
/>
<img
:src="logoUrl"
alt="Logo"
class="qr-logo"
/>
</div>
</div>
</template>
<style scoped>
.qr-logo-container {
text-align: center;
padding: 20px;
}
.qr-wrapper {
position: relative;
display: inline-block;
margin: 20px 0;
}
.qr-logo {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60px;
height: 60px;
background: white;
padding: 5px;
border-radius: 8px;
}
</style>
Dynamic QR Generation
vCard QR Code
<!-- VCardQR.vue -->
<script setup>
import { ref, computed } from 'vue'
import QrcodeVue from 'qrcode.vue3'
const name = ref('')
const phone = ref('')
const email = ref('')
const vCardData = computed(() => {
if (!name.value) return ''
return `BEGIN:VCARD
VERSION:3.0
FN:${name.value}
TEL:${phone.value}
EMAIL:${email.value}
END:VCARD`
})
</script>
<template>
<div class="vcard-qr">
<h3>Generate vCard QR Code</h3>
<input v-model="name" placeholder="Name" class="input" />
<input v-model="phone" placeholder="Phone" class="input" />
<input v-model="email" placeholder="Email" class="input" />
<div v-if="vCardData" class="qr-display">
<qrcode-vue
:value="vCardData"
:size="256"
level="M"
/>
</div>
</div>
</template>
<style scoped>
.vcard-qr {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 2px solid #ddd;
border-radius: 8px;
}
.qr-display {
margin-top: 20px;
text-align: center;
}
</style>
WiFi QR Code
<!-- WiFiQR.vue -->
<script setup>
import { ref, computed } from 'vue'
import QrcodeVue from 'qrcode.vue3'
const ssid = ref('')
const password = ref('')
const encryption = ref('WPA')
const wifiData = computed(() => {
if (!ssid.value) return ''
return `WIFI:T:${encryption.value};S:${ssid.value};P:${password.value};;`
})
</script>
<template>
<div class="wifi-qr">
<h3>WiFi QR Code</h3>
<input
v-model="ssid"
placeholder="Network Name (SSID)"
class="input"
/>
<input
v-model="password"
type="password"
placeholder="Password"
class="input"
/>
<select v-model="encryption" class="input">
<option value="WPA">WPA/WPA2</option>
<option value="WEP">WEP</option>
<option value="nopass">No Password</option>
</select>
<div v-if="wifiData" class="qr-display">
<qrcode-vue
:value="wifiData"
:size="256"
level="H"
/>
</div>
</div>
</template>
<style scoped>
.wifi-qr {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 2px solid #ddd;
border-radius: 8px;
}
.qr-display {
margin-top: 20px;
text-align: center;
}
</style>
Scan from File
<!-- FileScannerQR.vue -->
<script setup>
import { ref } from 'vue'
import { Html5Qrcode } from 'html5-qrcode'
const result = ref('')
const error = ref('')
const onFileSelected = async (event) => {
const file = event.target.files[0]
if (!file) return
const html5QrCode = new Html5Qrcode('reader')
try {
const scanResult = await html5QrCode.scanFile(file, true)
result.value = scanResult
error.value = ''
} catch (err) {
error.value = 'Failed to scan QR code from image'
result.value = ''
}
}
</script>
<template>
<div class="file-scanner">
<h3>Scan QR from Image File</h3>
<input
type="file"
accept="image/*"
@change="onFileSelected"
class="file-input"
/>
<div v-if="result" class="result">
<h4>Result:</h4>
<p>{{ result }}</p>
</div>
<div v-if="error" class="error">
{{ error }}
</div>
</div>
</template>
<style scoped>
.file-scanner {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.file-input {
width: 100%;
padding: 10px;
margin: 20px 0;
}
.result {
margin-top: 20px;
padding: 15px;
background: #d4edda;
border-radius: 8px;
}
.error {
margin-top: 20px;
padding: 15px;
background: #f8d7da;
border-radius: 8px;
color: #721c24;
}
</style>
Composable for QR Operations
// composables/useQRCode.js
import { ref } from 'vue'
export function useQRCode() {
const qrValue = ref('')
const qrSize = ref(256)
const qrLevel = ref('M')
const downloadQR = () => {
const canvas = document.querySelector('canvas')
if (canvas) {
const url = canvas.toDataURL('image/png')
const link = document.createElement('a')
link.download = 'qrcode.png'
link.href = url
link.click()
}
}
const generateVCard = (name, phone, email) => {
return `BEGIN:VCARD
VERSION:3.0
FN:${name}
TEL:${phone}
EMAIL:${email}
END:VCARD`
}
const generateWiFi = (ssid, password, encryption = 'WPA') => {
return `WIFI:T:${encryption};S:${ssid};P:${password};;`
}
return {
qrValue,
qrSize,
qrLevel,
downloadQR,
generateVCard,
generateWiFi
}
}
Using the Composable
<script setup>
import { useQRCode } from '@/composables/useQRCode'
import QrcodeVue from 'qrcode.vue3'
const { qrValue, qrSize, downloadQR } = useQRCode()
</script>
<template>
<div>
<input v-model="qrValue" placeholder="Enter URL" />
<qrcode-vue
:value="qrValue"
:size="qrSize"
/>
<button @click="downloadQR">Download</button>
</div>
</template>
Scanner Composable
// composables/useQRScanner.js
import { ref, onUnmounted } from 'vue'
import { Html5QrcodeScanner } from 'html5-qrcode'
export function useQRScanner(elementId = 'qr-reader') {
const scannedResult = ref('')
const isScanning = ref(false)
const error = ref('')
let scanner = null
const onScanSuccess = (decodedText) => {
scannedResult.value = decodedText
if (scanner) {
scanner.clear()
isScanning.value = false
}
}
const onScanError = (err) => {
console.warn('Scan error:', err)
}
const startScanning = () => {
scanner = new Html5QrcodeScanner(
elementId,
{
fps: 10,
qrbox: { width: 250, height: 250 }
},
false
)
scanner.render(onScanSuccess, onScanError)
isScanning.value = true
error.value = ''
}
const stopScanning = () => {
if (scanner) {
scanner.clear()
isScanning.value = false
}
}
const restartScanning = () => {
scannedResult.value = ''
startScanning()
}
onUnmounted(() => {
stopScanning()
})
return {
scannedResult,
isScanning,
error,
startScanning,
stopScanning,
restartScanning
}
}
Using Scanner Composable
<script setup>
import { onMounted } from 'vue'
import { useQRScanner } from '@/composables/useQRScanner'
const {
scannedResult,
isScanning,
startScanning,
restartScanning
} = useQRScanner()
onMounted(() => {
startScanning()
})
</script>
<template>
<div>
<div id="qr-reader"></div>
<div v-if="scannedResult">
<p>Result: {{ scannedResult }}</p>
<button @click="restartScanning">Scan Again</button>
</div>
</div>
</template>
Multiple QR Codes
<!-- MultipleQR.vue -->
<script setup>
import { ref } from 'vue'
import QrcodeVue from 'qrcode.vue3'
const qrCodes = ref([
{ id: 1, text: 'https://example.com', color: '#000000' },
{ id: 2, text: 'https://github.com', color: '#000000' },
{ id: 3, text: 'https://vuejs.org', color: '#42b883' }
])
const addQR = () => {
qrCodes.value.push({
id: Date.now(),
text: '',
color: '#000000'
})
}
const removeQR = (id) => {
qrCodes.value = qrCodes.value.filter(qr => qr.id !== id)
}
</script>
<template>
<div class="multiple-qr">
<h3>Multiple QR Codes</h3>
<div
v-for="qr in qrCodes"
:key="qr.id"
class="qr-item"
>
<input
v-model="qr.text"
placeholder="Enter text"
class="input"
/>
<input
v-model="qr.color"
type="color"
class="color-picker"
/>
<qrcode-vue
:value="qr.text || 'Empty'"
:size="150"
:color="{ dark: qr.color }"
/>
<button @click="removeQR(qr.id)" class="remove-btn">
Remove
</button>
</div>
<button @click="addQR" class="add-btn">
Add QR Code
</button>
</div>
</template>
<style scoped>
.multiple-qr {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.qr-item {
display: flex;
align-items: center;
gap: 15px;
margin: 15px 0;
padding: 15px;
border: 2px solid #ddd;
border-radius: 8px;
}
.input {
flex: 1;
padding: 8px;
border: 2px solid #ddd;
border-radius: 4px;
}
.color-picker {
width: 50px;
height: 40px;
border: none;
cursor: pointer;
}
.remove-btn {
padding: 8px 16px;
background: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.add-btn {
padding: 12px 24px;
background: #42b883;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
margin-top: 20px;
}
</style>
Error Handling
<script setup>
import { ref } from 'vue'
import { Html5QrcodeScanner } from 'html5-qrcode'
const error = ref('')
const handlePermissionError = () => {
error.value = 'Camera permission denied. Please enable camera access.'
}
const handleCameraError = (err) => {
if (err.name === 'NotAllowedError') {
handlePermissionError()
} else if (err.name === 'NotFoundError') {
error.value = 'No camera found on this device.'
} else {
error.value = `Error accessing camera: ${err.message}`
}
}
const initScanner = async () => {
try {
const scanner = new Html5QrcodeScanner('reader', { fps: 10 }, false)
scanner.render(onSuccess, onError)
} catch (err) {
handleCameraError(err)
}
}
</script>
<template>
<div>
<div id="reader"></div>
<div v-if="error" class="error-message">
{{ error }}
</div>
</div>
</template>
Router Setup
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import QRManager from '@/components/QRManager.vue'
const routes = [
{
path: '/',
redirect: '/qr'
},
{
path: '/qr',
name: 'QRManager',
component: QRManager
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
Best Practices
-
Error Correction Levels:
- L: 7% damage tolerance
- M: 15% (default)
- Q: 25%
- H: 30% (use with logos)
-
QR Code Size: Minimum 200x200px for reliable scanning
-
Contrast: Always use high contrast (dark on light)
-
Camera Permissions: Handle permission errors gracefully
-
Cleanup: Always clear scanner in
onUnmounted -
Reactivity: Use
computedfor dynamic QR generation -
Performance: Use
v-ifinstead ofv-showfor scanner component
Quick Reference
Generate QR:
<qrcode-vue
:value="text"
:size="256"
level="M"
/>
Download QR:
const canvas = document.querySelector('canvas')
const url = canvas.toDataURL('image/png')
Start Scanner:
const scanner = new Html5QrcodeScanner('reader', { fps: 10 }, false)
scanner.render(onSuccess, onError)
Stop Scanner:
scanner.clear()
Special Formats:
- URL:
https://example.com - WiFi:
WIFI:T:WPA;S:Name;P:Pass;; - Email:
mailto:test@example.com - SMS:
SMSTO:+1234567890:Message - vCard:
BEGIN:VCARD\nVERSION:3.0\n...
Conclusion
Vue.js makes QR code generation and scanning simple with qrcode.vue3 and html5-qrcode. Both libraries integrate seamlessly with Vue’s reactivity system for dynamic QR code applications.
Related Articles
Generate and Scan QR Codes in Angular
Quick guide to generating and scanning QR codes in Angular browser applications. Simple examples with ngx-qrcode and html5-qrcode.
Vue.js Tutorial to Integrate jsPDF Library to Edit PDF in Browser
Complete guide to using jsPDF in Vue.js applications for creating and editing PDF documents directly in the browser.
How to Integrate Mozilla PDF.js in HTML: Build a PDF Viewer in Browser
Quick guide to integrating Mozilla PDF.js into your HTML application to build a functional PDF viewer directly in the browser.