Ever wondered how those clean, professional QR codes with logos in the middle are made? I recently built a square QR code generator that does everything right in your browser—no server involved. Let me walk you through how it works.
Why Build This in the Browser?
Here's the thing: most QR code generators send your data to a server, which then generates the QR code and sends it back. That means every link you encode gets processed on someone else's machine. For a business generating promo codes or a developer adding company logos, that's not ideal.
By keeping everything client-side, your data never leaves your device. Every link, every piece of text you encode stays private. Plus, it's faster—you don't have to wait for server round-trips. Just load the page, type your content, and download.
For teams handling sensitive links—internal company documents, client information, or anything you'd rather not send to third parties—this approach makes a real difference.
How the Square QR Code Generator Works
Here's the complete flow from user input to downloadable PNG:
Step 1: Capturing User Input
The interface is straightforward. Users paste content into a text area—one entry per line for batch processing:
const [text, setText] = useState("");
const [standardContentType, setStandardContentType] = useState<"none" | "logo" | "centerText">("none");
For batch generation, the code splits input by newline:
const lines = text.split("\n").filter((line) => line.trim());
Step 2: Handling the Center Content
The generator supports three approaches: standard (nothing in center), logo image, or text overlay.
Logo Upload
const handleLogoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onloadend = () => setLogoImage(reader.result as string);
reader.readAsDataURL(file);
};
Simple enough—reads the uploaded image as a base64 data URL, which the QR rendering library can directly use.
Text Overlay
For text in the center, the code creates an image on-the-fly using canvas:
export function createTextImage(text: string, color: string, fontSize: number): string {
const size = Math.max(fontSize * 3, 200);
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d")!;
ctx.font = `bold ${fontSize}px sans-serif`;
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(text, size / 2, size / 2);
return canvas.toDataURL("image/png");
}
This creates a square image with centered text, ready to embed into the QR code.
Step 3: QR Code Generation
The core generation uses the qr-code-styling library:
const qrCode = new QRCodeStyling({
width: size,
height: size,
type: "canvas",
shape: "square",
data,
image: imageUrl,
dotsOptions: {
color: dotColor,
type: "square",
},
backgroundOptions: {
color: bgColor,
},
imageOptions: {
crossOrigin: "anonymous",
margin: 10,
hideBackgroundDots: true,
imageSize: Math.max(0.01, Math.min(1, logoSize / 100)),
},
});
Key options explained:
- shape: "square" - gives us the classic square module pattern
- image - the optional logo/text overlay, centered automatically
- dotsOptions.color - the foreground color (default black, customizable)
- hideBackgroundDots: true - clears dots behind the logo for cleaner look
- imageSize - controls logo size (percentage-based)
Step 4: Exporting the Result
const blob = await qrCode.getRawData("png");
const url = URL.createObjectURL(blob);
const img = await loadImage(url);
URL.revokeObjectURL(url);
return imageToCanvas(img).toDataURL("image/png");
Important detail: the library returns a blob, but we convert it to a canvas then back to dataURL. Why the extra step? Because the raw blob might not play nice with all browsers when displaying in <img> tags. Converting through canvas ensures consistent rendering.
Step 5: Batch Download
For multiple QR codes at once:
const handleDownloadAll = async () => {
const zip = new JSZip();
qrCodes.forEach(({ url, name }) => {
const base64Data = url.replace(/^data:image\/png;base64,/, "");
zip.file(`${name}.png`, base64Data, { base64: true });
});
const content = await zip.generateAsync({ type: "blob" });
saveAs(content, "qrcodes.zip");
};
Uses JSZip to bundle all generated QR codes into a single ZIP file—clean and efficient for bulk workflows.
Color Customization
Users can customize three colors:
- Dot color (foreground) - the actual QR code modules
- Background color - the fill between modules
- Center text color - when using text overlay mode
All through simple HTML color pickers:
<input
type="color"
value={dotColor}
onChange={(e) => setDotColor(e.target.value)}
/>
Output Size Control
The generator handles standard square output sizes:
const [qrWidth, setQrWidth] = useState(300);
// Clamped between 100-2000 pixels
const size = Math.max(100, Math.min(2000, qrWidth));
300px is the default—good for digital use. For print, bump up to 1000-2000px.
Try It Yourself
The square QR code generator is live at QR code generator. Upload your logo, customize the colors, and generate in seconds.
Need batch processing? Just paste multiple URLs (one per line), and download them all at once.
All processing happens locally—your links never go anywhere near a server.

Top comments (0)