Includes GSTIN, HSN/SAC, CGST/SGST/IGST
Your Business
Bill To (Client)
Invoice Details
Items / Services
Description HSN/SAC Qty Rate (₹) GST % Amount
Subtotal₹ 0.00
CGST₹ 0.00
SGST₹ 0.00
Total₹ 0.00
Payment Details
✅ Invoice Ready
Your invoice PDF is ready. Files are never uploaded — generated entirely in your browser.

Free GST Invoice Generator for India — No Signup Required

Create professional GST-compliant invoices instantly in your browser. No account, no upload, no subscription. LovelyPDF generates your invoice as a PDF file entirely on your device — your business data never leaves your browser. Supports GSTIN, HSN/SAC codes, CGST/SGST/IGST split, UPI QR code, and all Indian states for place of supply.

Who Is This For?

Frequently Asked Questions

Do I need a GSTIN to use this?

No. Toggle off the GST switch for a simple invoice without GSTIN. This is suitable for freelancers earning below the GST threshold (₹20 lakh for services, ₹40 lakh for goods).

What is the difference between CGST+SGST and IGST?

If your business and client are in the same state, use CGST + SGST (intrastate). If they are in different states, use IGST (interstate). Select the GST type in the invoice details section.

Is my business data safe?

Yes. Everything runs in your browser using JavaScript. Your GSTIN, bank details, and client information are never sent to any server. The PDF is generated locally on your device.

Can I save my details for next time?

Your business details are automatically saved in your browser's local storage. Next time you open the tool, your name, GSTIN, address, and bank details will be pre-filled.

Related tools: Compress PDF · Merge PDF · Edit PDF

, EUR: 'EUR', GBP: 'GBP' }; return map[document.getElementById('currency').value] || 'Rs.'; } async function generateInvoice() { const fromName = document.getElementById('fromName').value.trim(); const toName = document.getElementById('toName').value.trim(); if (!fromName) { alert('Please enter your business name.'); return; } if (!toName) { alert('Please enter client name.'); return; } if (items.length === 0) { alert('Please add at least one item.'); return; } const btn = document.getElementById('generateBtn'); btn.disabled = true; const pw = document.getElementById('progressWrap'); pw.style.display = 'block'; document.getElementById('downloadBox').classList.remove('show'); const pf = document.getElementById('progressFill'); const pp = document.getElementById('progressPct'); function setProgress(p, txt) { pf.style.width = p + '%'; pp.textContent = p + '%'; } try { setProgress(10, 'Setting up...'); const { PDFDocument, rgb, StandardFonts } = PDFLib; const pdfDoc = await PDFDocument.create(); setProgress(20, 'Loading fonts...'); const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold); const font = await pdfDoc.embedFont(StandardFonts.Helvetica); const page = pdfDoc.addPage([595, 842]); // A4 const { width, height } = page.getSize(); const margin = 40; const isGST = document.getElementById('gstToggle').checked; const gstType = document.getElementById('gstType').value; const sym = getCurrSymbol(); const currency = document.getElementById('currency').value; // Colors const accentR = 232/255, accentG = 64/255, accentB = 26/255; const accent = rgb(accentR, accentG, accentB); const dark = rgb(0.1, 0.1, 0.1); const muted = rgb(0.5, 0.5, 0.5); const lightBg = rgb(0.97, 0.97, 0.97); const white = rgb(1, 1, 1); let y = height - margin; // ── Header bar ──────────────────────────────────────────────────────── page.drawRectangle({ x: 0, y: height - 80, width, height: 80, color: accent }); // Logo area page.drawText('LovelyPDF', { x: margin, y: height - 30, size: 10, font, color: rgb(1,1,1,0.7) }); page.drawText(isGST ? 'TAX INVOICE' : 'INVOICE', { x: margin, y: height - 55, size: 26, font: fontBold, color: white }); // Invoice number top right const invNum = document.getElementById('invoiceNum').value || 'INV-001'; const invNumW = fontBold.widthOfTextAtSize(invNum, 14); page.drawText(invNum, { x: width - margin - invNumW, y: height - 42, size: 14, font: fontBold, color: white }); const invDate = document.getElementById('invoiceDate').value || ''; const dateLabel = 'Date: ' + invDate; const dateLabelW = font.widthOfTextAtSize(dateLabel, 9); page.drawText(dateLabel, { x: width - margin - dateLabelW, y: height - 60, size: 9, font, color: rgb(1,1,1,0.8) }); y = height - 100; setProgress(35, 'Drawing details...'); // ── From / To boxes ─────────────────────────────────────────────────── const boxW = (width - margin*2 - 20) / 2; // FROM box page.drawRectangle({ x: margin, y: y - 120, width: boxW, height: 120, color: lightBg, borderColor: rgb(0.9,0.9,0.9), borderWidth: 0.5 }); page.drawText('FROM', { x: margin + 10, y: y - 16, size: 8, font: fontBold, color: accent }); page.drawText(pdfSafe(fromName), { x: margin + 10, y: y - 30, size: 11, font: fontBold, color: dark }); let fromY = y - 45; const fromGSTIN = document.getElementById('fromGSTIN').value.trim(); if (isGST && fromGSTIN) { page.drawText(pdfSafe('GSTIN: ' + fromGSTIN), { x: margin + 10, y: fromY, size: 8, font, color: muted }); fromY -= 12; } const fromAddr = document.getElementById('fromAddress').value.trim(); if (fromAddr) { fromAddr.split('\n').slice(0, 3).forEach(line => { page.drawText(pdfSafe(line.substring(0, 50)), { x: margin + 10, y: fromY, size: 8, font, color: muted }); fromY -= 11; }); } const fromEmail = document.getElementById('fromEmail').value.trim(); const fromPhone = document.getElementById('fromPhone').value.trim(); if (fromEmail) { page.drawText(pdfSafe(fromEmail), { x: margin + 10, y: fromY, size: 8, font, color: muted }); fromY -= 11; } if (fromPhone) { page.drawText(pdfSafe(fromPhone), { x: margin + 10, y: fromY, size: 8, font, color: muted }); } // TO box const toX = margin + boxW + 20; page.drawRectangle({ x: toX, y: y - 120, width: boxW, height: 120, color: lightBg, borderColor: rgb(0.9,0.9,0.9), borderWidth: 0.5 }); page.drawText('BILL TO', { x: toX + 10, y: y - 16, size: 8, font: fontBold, color: accent }); page.drawText(pdfSafe(document.getElementById('toName').value.trim()), { x: toX + 10, y: y - 30, size: 11, font: fontBold, color: dark }); let toY = y - 45; const toGSTIN = document.getElementById('toGSTIN').value.trim(); if (isGST && toGSTIN) { page.drawText(pdfSafe('GSTIN: ' + toGSTIN), { x: toX + 10, y: toY, size: 8, font, color: muted }); toY -= 12; } const toAddr = document.getElementById('toAddress').value.trim(); if (toAddr) { toAddr.split('\n').slice(0, 3).forEach(line => { page.drawText(pdfSafe(line.substring(0, 50)), { x: toX + 10, y: toY, size: 8, font, color: muted }); toY -= 11; }); } const toEmail = document.getElementById('toEmail').value.trim(); const toPhone = document.getElementById('toPhone').value.trim(); if (toEmail) { page.drawText(pdfSafe(toEmail), { x: toX + 10, y: toY, size: 8, font, color: muted }); toY -= 11; } if (toPhone) { page.drawText(pdfSafe(toPhone), { x: toX + 10, y: toY, size: 8, font, color: muted }); } y = y - 140; // Due date + Place of supply row const dueDate = document.getElementById('dueDate').value; const pos = document.getElementById('placeOfSupply').value; if (dueDate) { page.drawText('Due Date: ' + dueDate, { x: margin, y, size: 9, font, color: muted }); } if (isGST && pos) { page.drawText('Place of Supply: ' + pos, { x: margin + 200, y, size: 9, font, color: muted }); } y -= 20; setProgress(50, 'Adding items...'); // ── Items table header ───────────────────────────────────────────────── const colWidths = isGST ? [165, 60, 40, 70, 50, 70, 60] // desc, hsn, qty, rate, gst%, amount (gst type label) : [235, 0, 50, 80, 0, 80, 0]; // desc, -, qty, rate, -, amount const colX = [margin]; colWidths.forEach((w, i) => colX.push(colX[i] + w)); // Header row page.drawRectangle({ x: margin, y: y - 18, width: width - margin*2, height: 18, color: accent }); const headers = isGST ? ['Description', 'HSN/SAC', 'Qty', 'Rate', 'GST%', 'GST Amt', 'Total'] : ['Description', '', 'Qty', 'Rate', '', 'Amount', '']; headers.forEach((h, i) => { if (!h) return; page.drawText(h, { x: colX[i] + 4, y: y - 13, size: 7, font: fontBold, color: white }); }); y -= 18; // Item rows let subtotal = 0, totalTax = 0; items.forEach((item, idx) => { const base = item.qty * item.rate; const taxAmt = isGST ? base * item.gstPct / 100 : 0; const lineTotal = base + taxAmt; subtotal += base; totalTax += taxAmt; const rowH = 16; if (idx % 2 === 1) { page.drawRectangle({ x: margin, y: y - rowH, width: width - margin*2, height: rowH, color: rgb(0.97,0.97,0.97) }); } const vals = isGST ? [item.desc.substring(0,30), item.hsn, String(item.qty), item.rate.toFixed(2), item.gstPct+'%', taxAmt.toFixed(2), lineTotal.toFixed(2)] : [item.desc.substring(0,40), '', String(item.qty), item.rate.toFixed(2), '', base.toFixed(2), '']; vals.forEach((v, i) => { if (!v && !colWidths[i]) return; page.drawText(pdfSafe(v), { x: colX[i] + 4, y: y - 11, size: 8, font, color: dark }); }); y -= rowH; if (y < 150) { /* TODO: multi-page */ } }); // Border bottom of table page.drawLine({ start: {x:margin, y}, end: {x: width-margin, y}, thickness: 0.5, color: rgb(0.85,0.85,0.85) }); y -= 10; setProgress(65, 'Calculating totals...'); // ── Totals ───────────────────────────────────────────────────────────── const totalsX = width - margin - 180; let tY = y; function drawTotalRow(label, val, bold=false) { page.drawText(label, { x: totalsX, y: tY, size: 9, font: bold ? fontBold : font, color: bold ? dark : muted }); const valStr = sym + ' ' + val; const valW = (bold ? fontBold : font).widthOfTextAtSize(valStr, 9); page.drawText(valStr, { x: width - margin - valW, y: tY, size: 9, font: bold ? fontBold : font, color: bold ? dark : muted }); tY -= 14; } drawTotalRow('Subtotal', subtotal.toFixed(2)); if (isGST) { if (gstType === 'intrastate') { const half = totalTax / 2; drawTotalRow('CGST', half.toFixed(2)); drawTotalRow('SGST', half.toFixed(2)); } else { drawTotalRow('IGST', totalTax.toFixed(2)); } } tY -= 4; page.drawLine({ start: {x: totalsX, y: tY + 8}, end: {x: width - margin, y: tY + 8}, thickness: 0.5, color: rgb(0.8,0.8,0.8) }); const grandTotal = subtotal + totalTax; page.drawRectangle({ x: totalsX - 8, y: tY - 8, width: width - margin - totalsX + 8, height: 22, color: accent }); page.drawText('TOTAL ' + currency, { x: totalsX, y: tY, size: 10, font: fontBold, color: white }); const totalStr = sym + ' ' + grandTotal.toFixed(2); const totalW = fontBold.widthOfTextAtSize(totalStr, 11); page.drawText(totalStr, { x: width - margin - totalW, y: tY, size: 11, font: fontBold, color: white }); y = Math.min(y, tY - 20) - 20; setProgress(75, 'Adding payment details...'); // ── Payment / Bank ───────────────────────────────────────────────────── const bankName = document.getElementById('bankName').value.trim(); const bankAcc = document.getElementById('bankAcc').value.trim(); const bankIFSC = document.getElementById('bankIFSC').value.trim(); const upiId = document.getElementById('upiId').value.trim(); if (bankName || bankAcc || upiId) { page.drawText('PAYMENT DETAILS', { x: margin, y, size: 8, font: fontBold, color: accent }); y -= 14; if (bankName) { page.drawText(pdfSafe('Bank: ' + bankName), { x: margin, y, size: 8, font, color: muted }); y -= 12; } if (bankAcc) { page.drawText(pdfSafe('Account: ' + bankAcc), { x: margin, y, size: 8, font, color: muted }); y -= 12; } if (bankIFSC) { page.drawText(pdfSafe('IFSC: ' + bankIFSC.toUpperCase()), { x: margin, y, size: 8, font, color: muted }); y -= 12; } if (upiId) { page.drawText(pdfSafe('UPI: ' + upiId), { x: margin, y, size: 8, font, color: muted }); y -= 12; } } // ── UPI QR Code ──────────────────────────────────────────────────────── if (upiId) { setProgress(82, 'Embedding QR code...'); try { const qrDiv = document.getElementById('qrCode'); const qrCanvas = qrDiv.querySelector('canvas'); if (qrCanvas) { const qrDataUrl = qrCanvas.toDataURL('image/png'); const qrBase64 = qrDataUrl.split(',')[1]; const qrBytes = Uint8Array.from(atob(qrBase64), c => c.charCodeAt(0)); const qrImage = await pdfDoc.embedPng(qrBytes); const qrX = width - margin - 80; const qrY = y - 80 < 100 ? 100 : y - 80; page.drawImage(qrImage, { x: qrX, y: qrY, width: 80, height: 80 }); page.drawText('Scan to pay', { x: qrX + 8, y: qrY - 12, size: 7, font, color: muted }); } } catch(e) {} } // ── Notes ───────────────────────────────────────────────────────────── const notes = document.getElementById('notes').value.trim(); if (notes) { y -= 10; page.drawText('NOTES', { x: margin, y, size: 8, font: fontBold, color: accent }); y -= 14; notes.split('\n').slice(0, 4).forEach(line => { page.drawText(pdfSafe(line.substring(0, 80)), { x: margin, y, size: 8, font, color: muted }); y -= 12; }); } // ── Footer ──────────────────────────────────────────────────────────── page.drawLine({ start: {x: margin, y: 35}, end: {x: width - margin, y: 35}, thickness: 0.5, color: rgb(0.9,0.9,0.9) }); page.drawText('Generated with LovelyPDF.in \u2014 100% free, no upload, no account', { x: margin, y: 22, size: 7, font, color: rgb(0.7,0.7,0.7) }); setProgress(92, 'Saving PDF...'); const pdfBytes = await pdfDoc.save(); const blob = new Blob([pdfBytes], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); const invNumClean = invNum.replace(/[^a-zA-Z0-9-_]/g, '_'); document.getElementById('downloadBtn').onclick = () => { const a = document.createElement('a'); a.href = url; a.download = 'Invoice-' + invNumClean + '.pdf'; a.click(); }; // Increment invoice number const parts = invNum.match(/(\d+)$/); if (parts) { const next = String(parseInt(parts[1]) + 1).padStart(3, '0'); localStorage.setItem('lpdf-inv-num', next); } setProgress(100, 'Done!'); setTimeout(() => { pw.style.display = 'none'; document.getElementById('downloadBox').classList.add('show'); btn.disabled = false; }, 400); } catch(err) { console.error(err); alert('Error generating invoice: ' + err.message); document.getElementById('progressWrap').style.display = 'none'; document.getElementById('generateBtn').disabled = false; } }