QR Code Sharing Component
Overview
The QRCodeDialog component provides a simple way to share AI assistants via QR codes. Users can quickly generate QR codes that link directly to specific assistants, making it easy to share assistant configurations across devices and with other users.
Features
📱 QR Code Generation
- Assistant Sharing: Generate QR codes that link directly to specific assistant instances
- Multiple Formats: QR code display, downloadable PNG, and copyable URL
- Cross-Device Access: Easy access to assistants across mobile and desktop devices
- Native Sharing: Integrates with device’s native share functionality when available
🔗 Sharing Options
- QR Code Display: Large, scannable QR code with 200px size
- URL Copy: One-click copy of the assistant URL to clipboard
- PNG Download: Download QR code as a high-quality PNG image (600x600px)
- Native Share: Use device’s built-in share menu when supported
Component Interface
Props
interface QRCodeDialogProps {
isOpen: boolean; // Controls dialog visibility
onOpenChange: (open: boolean) => void; // Callback when dialog state changes
assistantId: string; // Unique identifier for the assistant
assistantName: string; // Display name of the assistant
}
Usage Example
import { QRCodeDialog } from "@/components/QRCodeDialog";
// In your component
const [qrDialogOpen, setQrDialogOpen] = useState(false);
<QRCodeDialog
isOpen={qrDialogOpen}
onOpenChange={setQrDialogOpen}
assistantId="assistant-uuid-123"
assistantName="My AI Assistant"
/>
Technical Implementation
Dependencies
// External dependencies
import { QRCodeSVG } from 'qrcode.react';
// Internal components
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { useToast } from "@/components/hooks/use-toast";
// Icons
import { Copy, Check, Download, Share2 } from "lucide-react";
URL Generation
// Generates the shareable URL for the assistant
const shareUrl = `${window.location.origin}/assistant/${assistantId}`;
QR Code Configuration
<QRCodeSVG
id="qr-code-svg"
value={shareUrl}
size={200}
level="M" // Medium error correction
includeMargin={true}
/>
Core Functionality
1. Copy to Clipboard
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(shareUrl);
setCopied(true);
toast({
title: "Copied!",
description: "Share URL copied to clipboard"
});
setTimeout(() => setCopied(false), 2000);
} catch (error) {
// Error handling with toast notification
}
};
2. Download as PNG
The download functionality converts the SVG QR code to a high-quality PNG:
const handleDownload = () => {
// 1. Get the QR code SVG element
const svg = document.querySelector('#qr-code-svg') as SVGElement;
// 2. Clone and add white background
const svgClone = svg.cloneNode(true) as SVGElement;
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('width', '100%');
rect.setAttribute('height', '100%');
rect.setAttribute('fill', 'white');
svgClone.insertBefore(rect, svgClone.firstChild);
// 3. Create high-resolution canvas (3x scale)
const scale = 3;
const canvas = document.createElement('canvas');
canvas.width = 200 * scale;
canvas.height = 200 * scale;
// 4. Convert SVG to data URL and draw on canvas
const svgData = new XMLSerializer().serializeToString(svgClone);
const svgDataUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
// 5. Trigger download with sanitized filename
const filename = `${assistantName.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_qr_code.png`;
};
3. Native Share
const handleShare = async () => {
if (navigator.share) {
try {
await navigator.share({
title: assistantName,
text: `Check out this AI assistant: ${assistantName}`,
url: shareUrl,
});
} catch (error) {
console.error("Failed to share:", error);
}
} else {
// Fallback to copy URL if native share not available
handleCopy();
}
};
Integration
Used in SharingStatus Component
The QRCodeDialog is integrated into the SharingStatus component and appears when:
- Assistant has a valid
assistantIdandassistantName - Assistant access control is NOT private (public, domain, group, or specific users)
// From SharingStatus.tsx
{assistantId && assistantName && accessControl.type !== 'private' && (
<TooltipProvider>
<Tooltip delayDuration={300}>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="sm"
onClick={() => setQrDialogOpen(true)}
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
>
<QrCode className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
Show QR code for sharing
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
User Experience
Dialog Layout
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="text-center">Share QR Code</DialogTitle>
</DialogHeader>
<div className="flex flex-col items-center space-y-6 py-4">
{/* Assistant Name */}
<div className="text-center">
<h3 className="font-semibold text-lg">{assistantName}</h3>
<p className="text-sm text-muted-foreground">Scan to access this assistant</p>
</div>
{/* QR Code with styling */}
<div className="bg-white p-6 rounded-lg shadow-lg border-2 border-muted">
<QRCodeSVG {...qrCodeProps} />
</div>
{/* URL Display and Copy */}
<div className="w-full space-y-2">
<label className="text-sm font-medium text-muted-foreground">Share URL:</label>
<div className="flex gap-2">
<input type="text" value={shareUrl} readOnly />
<Button onClick={handleCopy}>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
</div>
{/* Action Buttons */}
<div className="flex flex-wrap justify-center gap-3 w-full">
<Button onClick={handleDownload}>
<Download className="w-4 h-4 mr-2" />
Download
</Button>
<Button onClick={handleShare}>
<Share2 className="w-4 h-4 mr-2" />
Share
</Button>
</div>
</div>
</DialogContent>
</Dialog>
Visual Design
- QR Code Styling: White background with padding, rounded corners, and shadow
- Responsive Design: Works on both desktop and mobile devices
- Button States: Visual feedback for copy action (checkmark when copied)
- Accessibility: Proper ARIA labels and keyboard navigation
Security Considerations
URL Security
- Origin-based URLs: Uses
window.location.originto generate appropriate URLs - No sensitive data: QR codes only contain the public assistant URL
- Access control: Respects existing assistant access permissions
File Downloads
- Safe filenames: Assistant names are sanitized for filesystem compatibility
- CSP compliance: Uses data URLs that comply with Content Security Policy
- High resolution: 3x scale provides crisp images while maintaining reasonable file size
Error Handling
Copy Functionality
try {
await navigator.clipboard.writeText(shareUrl);
// Success feedback
} catch (error) {
console.error("Failed to copy URL:", error);
toast({
title: "Copy failed",
description: "Failed to copy URL to clipboard",
variant: "destructive"
});
}
Download Functionality
// Multiple error checks throughout download process
if (!svg) {
console.error("QR code SVG not found");
return;
}
if (!ctx) {
console.error("Canvas context not available");
return;
}
img.onerror = () => {
console.error("Failed to load SVG image");
toast({
title: "Download failed",
description: "Failed to process QR code image",
variant: "destructive"
});
};
Browser Compatibility
Required Features
- QR Code Generation: SVG support (all modern browsers)
- Clipboard API:
navigator.clipboard(HTTPS required) - Canvas API: For PNG download functionality
- File Download:
URL.createObjectURLand blob support
Fallback Behavior
- Share API: Falls back to copy-to-clipboard if
navigator.shareunavailable - Manual copy: Users can manually select and copy the URL if clipboard API fails
- Mobile QR scanning: Relies on device camera and QR code reader apps
File Locations
- Component:
src/components/QRCodeDialog.tsx - Integration:
src/components/SharingStatus.tsx - Documentation:
docs/features/QR_CODE_SHARING.md
Related Components
- SharingStatus: Displays assistant sharing status and QR code trigger
- Dialog components: Uses shadcn/ui dialog system
- Toast system: Provides user feedback for actions