Aggiornamento: Generare QR Code con HTML, CSS e JavaScript (2026)
Perché generare QR Code con JavaScript nel 2026
Tre anni fa ho scritto il primo articolo su come generare QR Code con HTML, CSS e JavaScript e da allora ha accumulato quasi 600 visualizzazioni. Non male per un argomento apparentemente di nicchia. Ma il web è cambiato, le librerie si sono evolute e soprattutto sono arrivate richieste sempre più frequenti: come aggiungo il logo al QR Code? Come lo personalizzo con colori custom? Esiste un modo più moderno senza dipendenze esterne?
Nel 2026 i QR Code sono diventati uno strumento ancora più diffuso: li troviamo nei menu dei ristoranti, sui packaging, sulle landing page di micro SaaS, nelle campagne di AI marketing e perfino sulle business card digitali dei freelance. Saper generarli lato client, in puro JavaScript, senza backend e senza API a pagamento, è una competenza concreta che fa risparmiare tempo e denaro. In questo aggiornamento vedremo le tecniche più recenti: dalla libreria qrcode via CDN alla generazione su Canvas con logo sovrapposto, fino agli stili SVG personalizzati. Il tutto con codice reale, pronto da copiare e integrare nel tuo progetto.
Setup base: libreria qrcode.js e primo QR Code
Il punto di partenza più solido nel 2026 rimane la libreria qrcode.js (o il fork moderno qrcode su npm). è leggera, zero dipendenze, e supporta sia la generazione su Canvas che su SVG. Partiamo dal setup più semplice possibile: un file HTML standalone, nessun bundler, nessun framework.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generatore QR Code</title>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
</head>
<body>
<div id="qr-container">
<canvas id="qr-canvas"></canvas>
</div>
<script>
QRCode.toCanvas(
document.getElementById('qr-canvas'),
'https://cyberalchimista.it',
{
width: 300,
margin: 2,
color: {
dark: '#0f172a',
light: '#ffffff'
}
},
function(err) {
if (err) console.error('Errore QR:', err);
else console.log('QR generato con successo');
}
);
</script>
</body>
</html>Questo è il fondamento. La funzione QRCode.toCanvas() accetta: l'elemento DOM target, la stringa da codificare, un oggetto opzioni e una callback per la gestione degli errori. Il parametro margin controlla il quiet zone (bordo bianco obbligatorio per la leggibilità) — non scendere sotto 2 se vuoi che i lettori lo scansionino correttamente. Il parametro color.dark è il colore dei moduli (i quadratini neri), mentre color.light è lo sfondo.
QR Code con stili personalizzati e colori del brand
Il QR Code nero su bianco funziona, ma nel 2026 i designer vogliono qualcosa che si integri col brand. Possiamo personalizzare colori, dimensioni e aggiungere un wrapper CSS che lo renda visivamente coerente con il resto dell'interfaccia. Il punto critico: il contrasto tra dark e light deve essere almeno 3:1 per garantire la leggibilità degli scanner. Non usare colori pastello o simili tra loro.
// Generatore QR con stili dinamici e validazione contrasto
async function generateStyledQR(url, options = {}) {
const defaults = {
width: 280,
margin: 3,
errorCorrectionLevel: 'H', // Alto: necessario se aggiungi un logo
color: {
dark: '#1e293b', // slate-800
light: '#f0f9ff' // sky-50
}
};
const config = { ...defaults, ...options };
try {
// Genera come data URL (base64)
const dataURL = await QRCode.toDataURL(url, config);
return dataURL;
} catch (err) {
throw new Error(`QR generation failed: ${err.message}`);
}
}
// Utilizzo
generateStyledQR('https://cyberalchimista.it/landing-page-micro-saas-struttura-converte/', {
width: 320,
color: {
dark: '#06b6d4', // cyan del brand
light: '#001525' // sfondo scuro
}
}).then(dataURL => {
const img = document.createElement('img');
img.src = dataURL;
img.alt = 'QR Code CyberAlchimista';
document.getElementById('qr-wrapper').appendChild(img);
});
Nota importante: ho impostato errorCorrectionLevel: 'H'. Questo è il livello più alto di correzione errori (30% di ridondanza) ed è obbligatorio se intendi sovrapporre un logo al QR Code. Il livello H permette di oscurare fino al 30% dei moduli mantenendo la leggibilità. Se non aggiungi loghi, usa 'M' per un QR più compatto. Per approfondire tecniche avanzate di sviluppo frontend, dai un'occhiata alla mia guida CSS avanzato 2026.
Aggiungere un logo al centro del QR Code
Questa è la funzionalità più richiesta: il QR Code con logo aziendale al centro. La tecnica sfrutta la Canvas API nativa del browser per sovrapporre l'immagine del logo dopo la generazione del QR. è fondamentale farlo nel modo giusto per non compromettere la scansionabilità.
// QR Code con logo sovrapposto via Canvas API
async function generateQRWithLogo(url, logoSrc, canvasId) {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext('2d');
const size = 400;
canvas.width = size;
canvas.height = size;
// Step 1: genera QR su canvas temporaneo
const tempCanvas = document.createElement('canvas');
await QRCode.toCanvas(tempCanvas, url, {
width: size,
margin: 2,
errorCorrectionLevel: 'H',
color: { dark: '#1e293b', light: '#ffffff' }
});
// Step 2: disegna il QR sul canvas principale
ctx.drawImage(tempCanvas, 0, 0);
// Step 3: carica e sovrapponi il logo
const logo = new Image();
logo.crossOrigin = 'anonymous';
return new Promise((resolve, reject) => {
logo.onload = () => {
// Il logo deve coprire max il 20-25% del QR
const logoSize = size * 0.22;
const logoX = (size - logoSize) / 2;
const logoY = (size - logoSize) / 2;
// Sfondo bianco arrotondato dietro il logo
const padding = 8;
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.roundRect(
logoX - padding,
logoY - padding,
logoSize + padding * 2,
logoSize + padding * 2,
10
);
ctx.fill();
// Disegna il logo
ctx.drawImage(logo, logoX, logoY, logoSize, logoSize);
resolve(canvas.toDataURL('image/png'));
};
logo.onerror = () => reject(new Error('Logo load failed'));
logo.src = logoSrc;
});
}
// Utilizzo pratico
generateQRWithLogo(
'https://cyberalchimista.it',
'/assets/logo-icon.png',
'qr-canvas-logo'
).then(dataURL => {
console.log('QR con logo pronto:', dataURL.substring(0, 50) + '...');
}).catch(console.error);
Il trucco del roundRect() con sfondo bianco è fondamentale: crea un'area pulita attorno al logo che migliora sia l'estetica che la leggibilità. Nota anche l'uso di logo.crossOrigin = 'anonymous' — senza questo attributo, se il logo è su un dominio diverso, toDataURL() lancerà un errore CORS. Per capire meglio come gestire situazioni simili, ho scritto una guida su sicurezza frontend e CORS che consiglio di leggere.
Interfaccia completa: generatore QR con form e download
Mettiamo tutto insieme in un'interfaccia funzionale. Questo è un componente reale che puoi usare in qualsiasi progetto: form per l'input, preview live, pulsante di download in PNG. Il CSS usa variabili custom per facilitare il theming — una tecnica che tratto nel dettaglio nella mia guida ai CSS Custom Properties per temi dinamici.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Generatore QR Code 2026</title>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
<style>
:root {
--bg: #0f172a;
--surface: #1e293b;
--accent: #06b6d4;
--text: #e2e8f0;
--muted: #64748b;
--radius: 12px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Inter', system-ui, sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.qr-generator {
background: var(--surface);
border: 1px solid rgba(6, 182, 212, 0.2);
border-radius: var(--radius);
padding: 2rem;
width: 100%;
max-width: 480px;
}
h1 {
font-size: 1.5rem;
margin-bottom: 1.5rem;
color: var(--accent);
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
font-size: 0.875rem;
color: var(--muted);
margin-bottom: 0.5rem;
}
input, select {
width: 100%;
background: var(--bg);
border: 1px solid rgba(6, 182, 212, 0.3);
border-radius: 8px;
color: var(--text);
padding: 0.625rem 0.875rem;
font-size: 0.875rem;
outline: none;
transition: border-color 0.2s;
}
input:focus, select:focus {
border-color: var(--accent);
}
.color-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.color-picker {
display: flex;
align-items: center;
gap: 0.5rem;
}
input[type="color"] {
width: 40px;
height: 40px;
padding: 2px;
cursor: pointer;
border-radius: 6px;
flex-shrink: 0;
}
button {
width: 100%;
background: var(--accent);
color: #001525;
border: none;
border-radius: 8px;
padding: 0.75rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
margin-top: 1rem;
transition: opacity 0.2s;
}
button:hover { opacity: 0.85; }
button.secondary {
background: transparent;
color: var(--accent);
border: 1px solid var(--accent);
margin-top: 0.5rem;
}
.qr-preview {
display: flex;
justify-content: center;
margin-top: 1.5rem;
min-height: 180px;
align-items: center;
}
canvas {
border-radius: 8px;
max-width: 100%;
}
</style>
</head>
<body>
<div class="qr-generator">
<h1>Generatore QR Code</h1>
<div class="form-group">
<label for="qr-url">URL o testo da codificare</label>
<input type="text" id="qr-url"
placeholder="https://tuosito.it"
value="https://cyberalchimista.it">
</div>
<div class="form-group">
<label for="qr-size">Dimensione (px)</label>
<select id="qr-size">
<option value="200">200px</option>
<option value="300" selected>300px</option>
<option value="400">400px</option>
<option value="512">512px</option>
</select>
</div>
<div class="form-group color-row">
<div>
<label>Colore moduli</label>
<div class="color-picker">
<input type="color" id="color-dark" value="#0f172a">
<input type="text" id="color-dark-hex" value="#0f172a"
style="flex:1">
</div>
</div>
<div>
<label>Sfondo</label>
<div class="color-picker">
<input type="color" id="color-light" value="#ffffff">
<input type="text" id="color-light-hex" value="#ffffff"
style="flex:1">
</div>
</div>
</div>
<button onclick="generateQR()">Genera QR Code</button>
<button class="secondary" onclick="downloadQR()">
Scarica PNG
</button>
<div class="qr-preview">
<canvas id="qr-canvas"></canvas>
</div>
</div>
<script>
// Sync color pickers con input hex
['dark', 'light'].forEach(type => {
const picker = document.getElementById(`color-${type}`);
const hex = document.getElementById(`color-${type}-hex`);
picker.addEventListener('input', () => hex.value = picker.value);
hex.addEventListener('input', () => {
if (/^#[0-9a-fA-F]{6}$/.test(hex.value)) {
picker.value = hex.value;
}
});
});
async function generateQR() {
const url = document.getElementById('qr-url').value.trim();
if (!url) return alert('Inserisci un URL o del testo');
const size = parseInt(document.getElementById('qr-size').value);
const darkColor = document.getElementById('color-dark').value;
const lightColor = document.getElementById('color-light').value;
const canvas = document.getElementById('qr-canvas');
try {
await QRCode.toCanvas(canvas, url, {
width: size,
margin: 2,
errorCorrectionLevel: 'M',
color: { dark: darkColor, light: lightColor }
});
} catch (err) {
alert('Errore: ' + err.message);
}
}
function downloadQR() {
const canvas = document.getElementById('qr-canvas');
if (!canvas.toDataURL) return alert('Prima genera un QR Code');
const url = canvas.toDataURL('image/png');
if (url === 'data:,') return alert('Prima genera un QR Code');
const a = document.createElement('a');
a.href = url;
a.download = 'qrcode.png';
a.click();
}
// Genera subito all'avvio
generateQR();
</script>
</body>
</html>Generare QR Code in formato SVG
Il Canvas è ottimo per l'output raster (PNG/JPG), ma per casi d'uso come la stampa ad alta risoluzione o l'embedding in layout SVG, conviene usare il formato SVG. La stessa libreria supporta entrambi i formati con la funzione QRCode.toString().
// Generazione QR in formato SVG
async function generateQRSVG(text, containerId) {
const options = {
type: 'svg',
width: 256,
margin: 2,
errorCorrectionLevel: 'M',
color: {
dark: '#0f172a',
light: '#f8fafc'
}
};
try {
const svgString = await QRCode.toString(text, options);
// Inietta l'SVG nel DOM
const container = document.getElementById(containerId);
container.innerHTML = svgString;
// Rendi l'SVG responsivo
const svgEl = container.querySelector('svg');
if (svgEl) {
svgEl.removeAttribute('height');
svgEl.removeAttribute('width');
svgEl.setAttribute('viewBox', `0 0 256 256`);
svgEl.style.width = '100%';
svgEl.style.height = 'auto';
}
return svgString;
} catch (err) {
console.error('SVG QR error:', err);
throw err;
}
}
// Utilizzo
generateQRSVG('https://cyberalchimista.it/seo-tecnica-developer-checklist-2026/', 'qr-svg-container')
.then(() => console.log('SVG QR inserito nel DOM'))
.catch(console.error);
L'SVG ha il vantaggio di essere scalabile: perfetto per QR Code su landing page o materiali stampa dove devi garantire la massima qualità a qualsiasi dimensione. Ho rimosso width e height fissi e aggiunto un viewBox per renderlo responsivo — una best practice che si applica a tutti gli SVG inline. Se stai lavorando su un progetto con Supabase e hai bisogno di salvare questi QR generati dinamicamente, dai un'occhiata alla guida su Supabase nel 2026.
Integrazione in React e framework moderni
Nel 2026 la maggior parte dei progetti usa React, Next.js o framework simili. Per questi contesti esiste il pacchetto react-qr-code oppure si può usare direttamente qrcode con gli hook di React. Ecco il pattern più pulito con useEffect e gestione async corretta.
// QR Code Component per React / Next.js
import { useEffect, useRef, useState } from 'react';
// Carica la libreria lazy (evita SSR issues in Next.js)
let QRCodeLib = null;
async function getQRLib() {
if (!QRCodeLib) {
const mod = await import('qrcode');
QRCodeLib = mod.default || mod;
}
return QRCodeLib;
}
export function QRCodeCanvas({ value, size = 280, darkColor = '#0f172a', lightColor = '#ffffff', className = '' }) {
const canvasRef = useRef(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!value || !canvasRef.current) return;
let cancelled = false;
async function generate() {
setLoading(true);
setError(null);
try {
const QRCode = await getQRLib();
if (cancelled) return;
await QRCode.toCanvas(canvasRef.current, value, {
width: size,
margin: 2,
errorCorrectionLevel: 'M',
color: { dark: darkColor, light: lightColor }
});
} catch (err) {
if (!cancelled) setError(err.message);
} finally {
if (!cancelled) setLoading(false);
}
}
generate();
return () => { cancelled = true; };
}, [value, size, darkColor, lightColor]);
if (error) return (
<div style={{ color: 'red', padding: '1rem' }}>
Errore QR: {error}
</div>
);
return (
<div className={className} style={{ position: 'relative', display: 'inline-block' }}>
{loading && (
<div style={{
position: 'absolute', inset: 0,
background: '#f8fafc',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: '0.875rem', color: '#64748b'
}}>
Generazione...
</div>
)}
<canvas ref={canvasRef} />
</div>
);
}
// Utilizzo nel componente parent
export default function MyPage() {
const [url, setUrl] = useState('https://cyberalchimista.it');
return (
<div>
<input
value={url}
onChange={e => setUrl(e.target.value)}
placeholder="Inserisci URL..."
/>
<QRCodeCanvas
value={url}
size={300}
darkColor="#06b6d4"
lightColor="#001525"
/>
</div>
);
}
Alcune note su questo componente React: il cancelled flag nel cleanup di useEffect previene le race condition (aggiornamenti di stato su componenti smontati). Il caricamento lazy della libreria con import() dinamico risolve i problemi di SSR in Next.js, dove le API del browser non sono disponibili. Se stai costruendo un micro SaaS che include la generazione di QR Code come feature, questa architettura si integra perfettamente con i pattern per micro SaaS da sviluppatore solitario che ho descritto in un articolo dedicato.
FAQ e Domande Frequenti
Qual è la migliore libreria per generare QR Code in JavaScript nel 2026?
La libreria qrcode (npm) è la scelta più solida nel 2026: attivamente mantenuta, zero dipendenze esterne, supporto per Canvas, SVG e Data URL, funziona sia nel browser che in Node.js. Per progetti React, react-qr-code è un wrapper comodo che semplifica l'integrazione con i componenti. Se hai bisogno di QR Code esteticamente molto personalizzati (gradient, forme custom dei moduli), considera qr-code-styling che offre opzioni di styling avanzate ma ha un bundle più pesante (~150KB). Per la maggior parte dei casi d'uso standard, qrcode con le opzioni base è sufficiente e pesa solo ~15KB.
Come aggiungo un logo al QR Code senza renderlo illeggibile?
La regola d'oro è: imposta sempre errorCorrectionLevel: 'H' prima di sovrapporre il logo, e assicurati che il logo copra al massimo il 20-25% della superficie totale del QR Code. Il livello H permette fino al 30% di ridondanza, quindi anche se il logo copre parte dei moduli, il codice rimane leggibile. Aggiungi sempre uno sfondo bianco attorno al logo (padding di almeno 6-8px) per separarlo visivamente dai moduli circostanti. Evita loghi con trasparenza che si sovrappongono ai moduli scuri — il contrasto potrebbe confondere lo scanner. Dopo aver aggiunto il logo, testalo sempre con almeno 3 scanner diversi (fotocamera iOS, Android e un'app dedicata).
Posso generare QR Code senza librerie esterne, in vanilla JS?
Tecnicamente sì, ma è altamente sconsigliato. L'algoritmo QR Code è complesso: include codifiche dati multiple (numerico, alfanumerico, byte, kanji), calcolo Reed-Solomon per la correzione errori, maschere di pattern e placement di marker specifici. Implementarlo da zero in vanilla JS richiederebbe centinaia di righe di codice e mesi di debug. Le librerie esistenti hanno già risolto tutti questi problemi e sono state testate con milioni di scanner reali. La linea guida moderna è: non reinventare standard ben consolidati. Usa la libreria, personalizza l'output tramite le opzioni disponibili e focalizzati sul valore che aggiungi al tuo prodotto.
Come ottimizzare le performance se genero molti QR Code?
Se devi generare QR Code in bulk (ad esempio per un sistema e-commerce con centinaia di prodotti), sposta la generazione in un Web Worker per non bloccare il thread principale. La libreria qrcode gira anche in Worker context. In alternativa, genera i QR lato server con Node.js usando la stessa libreria e serve le immagini statiche via CDN — molto più efficiente per contenuti che non cambiano frequentemente. Per la generazione lato client in React, usa useMemo o useCallback per memoizzare i risultati e rigenera il QR solo quando l'input cambia effettivamente, evitando render inutili. Un cache semplice con una Map keyed sull'URL può ridurre drasticamente le chiamate ridondanti.
Conclusione
Generare QR Code con JavaScript nel 2026 è più facile e flessibile che mai. Dall'HTML statico all'integrazione React, dalla generazione Canvas con logo all'output SVG scalabile: le librerie disponibili coprono ogni caso d'uso con un'API pulita e zero dipendenze aggiuntive. I punti chiave da portare a casa: usa sempre errorCorrectionLevel: 'H' se aggiungi elementi sovrapposti, garantisci un contrasto minimo 3:1 tra moduli e sfondo, e testa sempre il risultato con scanner reali prima di andare in produzione. Se integri i QR Code in un'applicazione più complessa — un micro SaaS, un'app di marketing, una landing page — considera la generazione server-side per i contenuti statici e riserva quella client-side solo per i QR dinamici e personalizzati dall'utente. Buon coding.
Suggerimenti e Risorse
🔧 Strumento: qrcode su npm — la libreria di riferimento nel 2026, con supporto Canvas, SVG e Node.js. Zero dipendenze, ~15KB.
💡 Pro tip: Per QR Code con stili avanzati (moduli rotondi, gradient, bordi decorativi) usa qr-code-styling. Bundle più pesante ma output visivamente premium, ideale per landing page di prodotto.
🎯 Best practice: Prima del deploy, testa sempre il QR Code con 3 scanner diversi (iOS Camera, Google Lens, un'app QR dedicata) e su stampe fisiche se prevedi utilizzo offline. Un QR imperfetto online potrebbe essere illeggibile su carta.

