qr 5
import React, { useState, useEffect, useRef } from 'react';
import QRCodeStyling, {
DrawType,
TypeNumber,
Mode,
ErrorCorrectionLevel,
DotType,
CornerSquareType,
CornerDotType,
FileExtension
} from 'qr-code-styling';
import { CONTENT_TYPES, ContentType, QRStyleOptions, cn } from '../lib/utils';
import { InputForm } from './InputForms';
import { StyleCustomizer } from './StyleCustomizer';
import { Download, RefreshCw, CheckCircle2, AlertCircle } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import jsPDF from 'jspdf';
export const QRGenerator: React.FC = () => {
const [contentType, setContentType] = useState('url');
const [contentData, setContentData] = useState({});
const [styleOptions, setStyleOptions] = useState({
dotType: 'square',
cornerSquareType: 'square',
cornerDotType: 'square',
dotColor: '#000000',
bgColor: '#ffffff',
margin: 2,
showTitle: false,
});
const [downloadFormat, setDownloadFormat] = useState('svg');
const [qrCode, setQrCode] = useState(null);
const [isGenerated, setIsGenerated] = useState(false);
const [isGenerating, setIsGenerating] = useState(false);
const qrRef = useRef(null);
// Initialize QR code object
useEffect(() => {
const newQrCode = new QRCodeStyling({
width: 300,
height: 300,
type: 'svg' as DrawType,
data: '',
dotsOptions: {
color: '#000000',
type: 'square' as DotType,
},
backgroundOptions: {
color: '#ffffff',
},
imageOptions: {
crossOrigin: 'anonymous',
margin: 5,
}
});
setQrCode(newQrCode);
}, []);
// Update QR code when styles change (if already generated)
useEffect(() => {
if (qrCode && isGenerated) {
updateQRCode();
}
}, [styleOptions]);
const getQRData = () => {
switch (contentType) {
case 'url': return contentData.url || '';
case 'text': return contentData.text || '';
case 'email': return `mailto:${contentData.email || ''}?subject=${encodeURIComponent(contentData.subject || '')}&body=${encodeURIComponent(contentData.body || '')}`;
case 'phone': return `tel:${contentData.phone || ''}`;
case 'sms': return `sms:${contentData.phone || ''}?body=${encodeURIComponent(contentData.message || '')}`;
case 'wifi': return `WIFI:S:${contentData.ssid || ''};T:${contentData.encryption || 'WPA'};P:${contentData.password || ''};H:${contentData.hidden ? 'true' : 'false'};;`;
case 'social': {
const val = contentData.value || '';
switch (contentData.platform) {
case 'facebook': return `https://facebook.com/${val}`;
case 'instagram': return `https://instagram.com/${val}`;
case 'whatsapp': return `https://wa.me/${val}`;
case 'youtube': return `https://youtube.com/@${val}`;
case 'twitter_url': return `https://twitter.com/${val}`;
case 'twitter_tweet': return `https://twitter.com/intent/tweet?text=${encodeURIComponent(val)}`;
default: return val;
}
}
case 'location': return `geo:${contentData.lat || 0},${contentData.lng || 0}`;
case 'event': return `BEGIN:VEVENT\nSUMMARY:${contentData.title || ''}\nLOCATION:${contentData.location || ''}\nDTSTART:${contentData.start?.replace(/[-:]/g, '') || ''}\nDTEND:${contentData.end?.replace(/[-:]/g, '') || ''}\nEND:VEVENT`;
case 'pdf': return contentData.url || '';
case 'contact':
case 'vcard':
return `BEGIN:VCARD\nVERSION:3.0\nN:${contentData.lastName || ''};${contentData.firstName || ''};;${contentData.prefix || ''};\nFN:${contentData.prefix || ''} ${contentData.firstName || ''} ${contentData.lastName || ''}\nORG:${contentData.organization || ''}\nTITLE:${contentData.title || ''}\nTEL;TYPE=CELL:${contentData.phone || ''}\nEMAIL:${contentData.email || ''}\nEND:VCARD`;
case 'mecard':
return `MECARD:N:${contentData.lastName || ''},${contentData.firstName || ''};TEL:${contentData.phone || ''};EMAIL:${contentData.email || ''};;`;
default: return '';
}
};
const isFormValid = () => {
const data = getQRData();
return data.trim().length > 0;
};
const updateQRCode = () => {
if (!qrCode) return;
qrCode.update({
data: getQRData(),
dotsOptions: {
color: styleOptions.dotColor,
type: styleOptions.dotType as DotType,
},
backgroundOptions: {
color: styleOptions.bgColor,
},
cornersSquareOptions: {
color: styleOptions.dotColor,
type: styleOptions.cornerSquareType as CornerSquareType,
},
cornersDotOptions: {
color: styleOptions.dotColor,
type: styleOptions.cornerDotType as CornerDotType,
},
margin: styleOptions.margin,
image: styleOptions.logo,
});
};
const handleGenerate = () => {
if (!qrCode || !isFormValid()) return;
setIsGenerating(true);
setTimeout(() => {
updateQRCode();
if (qrRef.current) {
qrRef.current.innerHTML = '';
qrCode.append(qrRef.current);
}
setIsGenerated(true);
setIsGenerating(false);
// Scroll to result
qrRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 800);
};
const handleDownload = async () => {
if (!qrCode) return;
if (downloadFormat === 'pdf') {
const blob = await qrCode.getRawData('png');
if (blob) {
const reader = new FileReader();
reader.onloadend = () => {
const base64data = reader.result as string;
const pdf = new jsPDF();
pdf.addImage(base64data, 'PNG', 10, 10, 100, 100);
pdf.save(`qr-code-${Date.now()}.pdf`);
};
reader.readAsDataURL(blob);
}
} else {
qrCode.download({
name: `qr-code-${Date.now()}`,
extension: downloadFormat as FileExtension
});
}
};
return (
);
};
{/* Left Column: Inputs & Customization */}
{/* Right Column: Preview & Download */}
{isGenerated ? (
QR Code Ready
{/* QR Preview Container */}
) : (
)}
{/* Tips Card */}
{/* Content Type Selection */}
{/* Dynamic Input Form */}
{/* Style Customization */}
{/* Generate Button */}
{CONTENT_TYPES.map((type) => (
))}
{styleOptions.showTitle && styleOptions.title && (
{/* Download Controls */}
{styleOptions.title}
)}
By default SVG is selected. You can choose other formats from the dropdown.
No QR Code Generated
Fill in the information on the left and click "Generate" to see your QR code here.
Pro Tips
- • Use high contrast colors for better scanning.
- • SVG format is best for high-quality printing.
- • Adding a logo makes your QR code look professional.