HTML to PDF with Async JavaScript Execution: Complete Guide
Master async JavaScript execution in PDF generation. Learn the window.__RENDER_DONE__ pattern for reliable HTML to PDF conversion with QR codes, charts, and dynamic content.
Learn how to generate QR codes in PDF documents using HTML and JavaScript. This comprehensive guide covers everything from basic QR code generation to advanced use cases like payment QR codes, event tickets, and digital certificates.
QR codes in PDFs are essential for modern business documents - from invoices with payment links to event tickets with check-in codes. We'll show you how to create professional PDFs with embedded QR codes using popular JavaScript libraries and our PDF API.
QR codes in PDFs bridge the gap between physical and digital experiences. They enable instant actions like payments, website visits, or data verification directly from printed documents. Here are the most common use cases:
Several excellent JavaScript libraries can generate QR codes. Here's a comparison of the most popular options:
| Library | GitHub Stars | Best For | Key Features |
|---|---|---|---|
| qrcode | ~17,000 | ✅ Browser & Node.js | Simple API, SVG/PNG/Canvas, CDN available |
| qr-code-styling | ~3,000 | ✅ Browser & Node.js | Custom colors, logos, rounded corners |
| qrcodejs | ~12,000 | ✅ Browser only | Zero dependencies, simple, lightweight |
| node-qrcode | ~7,000 | ❌ Node.js only | Server-side, CLI support |
For this guide, we'll use qrcode (npm package) as it offers the best balance of features, browser/Node.js support, and ease of use. It's perfect for generating QR codes that will be embedded in PDFs.
✅ Browser Support
The qrcode library works perfectly in browsers! You can include it via CDN or use the precompiled browser build. All examples in this guide use browser-compatible code. No Node.js required for client-side generation.
Let's start with a simple example of generating a QR code and embedding it in an HTML document that can be converted to PDF.
For browser usage, include the library via CDN or install via npm:
npm install qrcodeOr use the browser build via CDN:
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.4/build/qrcode.min.js"></script>Generate a QR code as a base64 data URL that can be embedded directly in HTML:
// Browser usage (after including via CDN)
const qrCodeDataURL = await QRCode.toDataURL('https://customjs.space', {
errorCorrectionLevel: 'H',
type: 'image/png',
width: 200,
margin: 1
});
console.log(qrCodeDataURL); // data:image/png;base64,...Use the generated QR code in your HTML template:
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.qr-container { text-align: center; margin: 20px; }
.qr-code { border: 2px solid #333; padding: 10px; }
</style>
</head>
<body>
<div class="qr-container">
<h2>Scan to Visit Website</h2>
<img src="" class="qr-code" alt="QR Code" />
<p>Scan this code with your phone</p>
</div>
</body>
</html>Generate a PDF with a QR code that links to CustomJS website:
One of the most practical applications is adding payment QR codes to invoices. This allows customers to pay instantly by scanning the code with their banking app.
// EPC QR Code format for SEPA payments (Browser)
const invoiceTotal = '250.00';
const invoiceNumber = 'INV-2025-001';
const epcData = `BCD
002
1
SCT
COBADEFFXXX
Your Company Name
DE89370400440532013000
EUR${invoiceTotal}
CHAR
Invoice ${invoiceNumber}`;
const qrCode = await QRCode.toDataURL(epcData, {
errorCorrectionLevel: 'M',
width: 200
});<div class="invoice">
<h1>INVOICE #</h1>
<!-- Invoice details -->
<div class="details">
<p>Total: </p>
<p>Due Date: </p>
</div>
<!-- Payment QR Code -->
<div class="payment-section">
<h3>Pay with QR Code</h3>
<img src="" alt="Payment QR Code" />
<p>Scan to pay instantly via your banking app</p>
</div>
</div>Interactive example of an invoice with embedded payment QR code:
Event tickets with QR codes enable fast, contactless check-in. The QR code can contain ticket ID, attendee information, or a verification URL.
// Option 1: Simple ticket ID
const ticketData = 'TICKET-2025-ABC123';
// Option 2: JSON data (for app-based scanning)
const ticketJSON = JSON.stringify({
ticketId: 'TICKET-2025-ABC123',
eventId: 'EVT-001',
attendee: 'John Doe',
seat: 'A-15',
timestamp: Date.now()
});
// Option 3: Verification URL (recommended)
const verificationURL = `https://yourevent.com/verify?ticket=TICKET-2025-ABC123`;
const qrCode = await QRCode.toDataURL(verificationURL, {
errorCorrectionLevel: 'H', // High error correction for damaged tickets
width: 250
});<div class="ticket">
<div class="ticket-header">
<h1></h1>
<p> | </p>
</div>
<div class="ticket-body">
<p><strong>Attendee:</strong> </p>
<p><strong>Seat:</strong> </p>
<p><strong>Ticket ID:</strong> </p>
</div>
<div class="qr-section">
<img src="" alt="Ticket QR Code" />
<p>Present this code at entrance</p>
</div>
</div>Generate a professional event ticket PDF with QR code:
Add QR codes to certificates, diplomas, or credentials to enable instant verification of authenticity. This is crucial for preventing fraud and enabling employers or institutions to verify credentials.
// Generate unique certificate verification URL
const certificateData = {
id: 'CERT-2025-001',
recipient: 'Jane Smith',
course: 'Advanced Web Development',
issueDate: '2025-01-15',
issuer: 'Tech Academy'
};
// Create verification hash (use crypto-js in browser or server-side)
// For browser: import CryptoJS from 'crypto-js'
const dataString = JSON.stringify(certificateData);
const hash = CryptoJS.SHA256(dataString).toString();
// Verification URL with hash
const verificationURL = `https://academy.com/verify/${certificateData.id}?hash=${hash}`;
const qrCode = await QRCode.toDataURL(verificationURL, {
errorCorrectionLevel: 'H',
width: 200
});<div class="certificate">
<div class="certificate-border">
<h1>Certificate of Completion</h1>
<p class="recipient"></p>
<p>has successfully completed</p>
<h2></h2>
<p>Issue Date: </p>
<div class="verification">
<img src="" alt="Verification QR Code" />
<p>Scan to verify authenticity</p>
<p class="cert-id">ID: </p>
</div>
</div>
</div>vCard QR codes allow people to save your contact information directly to their phone by scanning. This is perfect for business cards, email signatures, and networking materials.
// vCard 3.0 format
const vCardData = `BEGIN:VCARD
VERSION:3.0
FN:John Doe
ORG:CustomJS
TITLE:Software Engineer
TEL;TYPE=WORK,VOICE:+1-555-123-4567
TEL;TYPE=CELL:+1-555-987-6543
EMAIL:john.doe@customjs.space
URL:https://customjs.space
ADR;TYPE=WORK:;;123 Tech Street;San Francisco;CA;94105;USA
END:VCARD`;
const qrCode = await QRCode.toDataURL(vCardData, {
errorCorrectionLevel: 'M',
width: 200
});<div class="business-card">
<div class="card-front">
<h2></h2>
<p class="title"></p>
<p class="company"></p>
<div class="contact-info">
<p></p>
<p></p>
<p></p>
</div>
<div class="qr-code">
<img src="" alt="Contact QR Code" />
<p>Scan to save contact</p>
</div>
</div>
</div>While basic black-and-white QR codes work perfectly, you can customize them to match your brand. Use the qr-code-styling library for advanced styling options.
Use the qr-code-styling library for advanced customization:
// Browser: include via CDN or npm
// <script src="https://cdn.jsdelivr.net/npm/qr-code-styling@1.6.0/lib/qr-code-styling.js"></script>
const qrCode = new QRCodeStyling({
width: 300,
height: 300,
data: "https://customjs.space",
image: "https://yoursite.com/logo.png", // Center logo
dotsOptions: {
color: "#4267b2",
type: "rounded" // Options: rounded, dots, classy, square
},
backgroundOptions: {
color: "#ffffff",
},
imageOptions: {
crossOrigin: "anonymous",
margin: 10
},
cornersSquareOptions: {
color: "#000000",
type: "extra-rounded"
},
cornersDotOptions: {
color: "#4267b2",
type: "dot"
}
});
// Get as data URL for embedding in HTML
qrCode.getRawData('png').then(blob => {
const reader = new FileReader();
reader.onload = () => {
const dataUrl = reader.result;
console.log(dataUrl); // Use in img src
};
reader.readAsDataURL(blob);
});QR codes have built-in error correction that allows them to be scanned even if partially damaged:
| Level | Recovery Capacity | Best For |
|---|---|---|
| L (Low) | ~7% damage | Clean environments, digital displays |
| M (Medium) | ~15% damage | General use, most applications |
| Q (Quartile) | ~25% damage | Outdoor use, industrial settings |
| H (High) | ~30% damage | Logos, heavy wear, critical applications |
When generating PDFs with dynamic content like QR codes, you might encounter this error:
Refused to execute script because its MIME type ('text/plain') is not executableWhy this happens:
Content-Type: text/plain instead of application/javascriptdocument.write or external <script src> can fail in sandboxed environmentsInstead of traditional CDN scripts:
<!-- ❌ This fails in headless browsers -->
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.4/build/qrcode.min.js"></script>Use ES Module imports with esm.sh:
<!-- ✅ This works reliably -->
<script type="module">
import QRCode from "https://esm.sh/qrcode@1.5.4";
const canvas = document.getElementById('qr-canvas');
await QRCode.toCanvas(canvas, 'https://example.com', {
width: 200
});
</script>Why ES Modules work:
Content-Type: application/javascriptdocument.write needed1. Inline the library:
<script>
// Paste entire qrcode.min.js content here
</script>2. Pre-bundle with esbuild:
esbuild --bundle --format=iife app.js > bundle.js3. Use CustomJS with window.__RENDER_DONE__:
For async content that needs to finish before PDF generation:
<script>
// Set flag EARLY in <head>
window.__RENDER_DONE__ = false;
</script>
<script type="module">
import QRCode from "https://esm.sh/qrcode@1.5.4";
await QRCode.toCanvas(canvas, url, options);
// Signal completion
window.__RENDER_DONE__ = true;
</script>📖 Learn more: Check out our guide on HTML to PDF with Async JS Execution for advanced techniques.
The qrcode npm package is the most popular and reliable choice, with ~17,000 GitHub stars. It works in both Node.js and browsers, supports multiple output formats (PNG, SVG, Canvas), and has excellent documentation. For styled QR codes with logos and custom colors, use qr-code-styling.
Yes! Use the qr-code-styling library to add logos. Keep the logo under 30% of the QR code area and use high error correction (Level H) to ensure scannability. Always test the QR code with multiple devices after adding a logo.
For European SEPA payments, use the EPC QR code format. For other regions, use UPI (India), Bitcoin addresses, or payment URLs (PayPal, Stripe). The QR code should contain payment details like amount, recipient, and reference number. See our invoice example above for complete code.
For printed PDFs, QR codes should be at least 2cm x 2cm (0.8" x 0.8"), with 3-4cm recommended for easy scanning. In digital PDFs, use 200x200px minimum, 300x300px recommended. The size should be 10% of the expected viewing/scanning distance.
Generate the QR code as a base64 data URL using QRCode.toDataURL(), then embed it in your HTML template using an <img> tag with the data URL as the src attribute. This ensures the QR code is embedded directly in the HTML without external dependencies.
Use Medium (M) for general applications, High (H) when adding logos or for critical applications like tickets and certificates, and Low (L) only for clean digital environments. Higher error correction allows the QR code to be scanned even if partially damaged.
Yes! Use the CustomJS API to automate PDF generation with QR codes. You can integrate with n8n, Make.com, or use our HTTP API directly. Generate the QR code in your workflow, embed it in HTML, and send it to our API for PDF conversion. Perfect for automated invoicing, ticketing, and certificate systems.
Use the vCard 3.0 format as shown in our business card example. Include fields like FN (full name), ORG (organization), TEL (phone), EMAIL, and URL. Generate the QR code from the vCard string, and when scanned, it will prompt users to save the contact directly to their phone.
Adding QR codes to PDF documents creates powerful bridges between physical and digital experiences. Whether you're generating invoices with payment QR codes, event tickets with check-in codes, or certificates with verification links, the combination of HTML, JavaScript QR libraries, and PDF generation creates professional, functional documents.
Key takeaways:
Ready to start generating PDFs with QR codes? Check out our PDF API documentation or try our n8n integration for no-code automation.
Continue reading on similar topics
Master async JavaScript execution in PDF generation. Learn the window.__RENDER_DONE__ pattern for reliable HTML to PDF conversion with QR codes, charts, and dynamic content.
Learn how to automate PDF generation in Make.com with CustomJS. Step-by-step guide with templates for invoices, HTML to PDF, and page extraction. 600 free PDFs/month.
Learn how to automate PDF generation in n8n workflows. Complete guide covering HTML to PDF conversion, invoice generation, and workflow templates.
Creating professional HTML invoices that convert perfectly to PDF is essential for any business. 5 beautiful invoice templates with live PDF generators.
Compare HTML to PDF conversion APIs for price, speed, and JavaScript support. CustomJS offers 600 free PDFs/month and easy Make.com integration.
Cost-effective alternative to QuickBooks & FreshBooks. Automatic PDF invoice generation with n8n, Make.com, or API integration.
Adding page numbers and footers to PDF documents. Explore 6 different approaches with interactive examples.<br>
Broken page breaks, margins, fonts, tables? Fix the most common HTML-to-PDF issues with practical CSS strategies and examples.
Master print styles with our Print CSS Cheatsheet. Learn tips, tricks, and code examples to create professional, print-ready HTML documents.
Master advanced JavaScript techniques in Power Automate. Learn async/await, npm packages, error handling, and real-world use cases. No Azure Functions needed.
Execute JavaScript code directly in Power Automate. Integrate JavaScript with CustomJS in 5 minutes – no Azure Functions needed.
If you want to improve your workflow in Make with JavaScript, there are some powerful tools available. Two of the best options are 0CodeKit and CustomJS.