Free Online Invoice Generator - Small Study Tools
Professional Invoices · Download as PDF · 100% Free
Free Online Invoice Generator for Small Businesses & Freelancers Create professional invoices in seconds. Fill in your details, add line items, and download a polished PDF. Pro Mode adds logo upload, colour themes, font choices, payment details, watermarks and more.
● Pro Version is Free for now
Simple
Pro — Full Customisation
✓ Logo · Themes · Fonts · Payment Details · Watermark · Signature unlocked
Everything runs in your browser — your invoice data is never sent to any server
🖼️ Logo & Colour Theme
Click to upload your logo (PNG, JPG, SVG)
Font Family
DM Sans Poppins Playfair Display Merriweather Georgia
👤 Bill To (Client Details)
📦 Line Items
+ Add Line Item 🔖 Watermark & Status
None
DRAFT
PAID
VOID
✍️ Signature
Draw your signature above or upload an image
📄 Download or preview your invoice
`;
const blob = new Blob([html],{type:'text/html'});
const a=document.createElement('a');
a.download = 'invoice-'+(g('invNumber')||'001')+'.html';
a.href=URL.createObjectURL(blob); a.click();
toast('Download ready — open the file and Print → Save as PDF');
}// ── SAVE DRAFT (Pro) ──────────────────────────────────────────
function saveDraft() {
const g = id => document.getElementById(id)?.value || '';
const draft = {
fromName:g('fromName'),fromAddress:g('fromAddress'),fromEmail:g('fromEmail'),fromPhone:g('fromPhone'),
fromWebsite:g('fromWebsite'),fromReg:g('fromReg'),
toName:g('toName'),toAddress:g('toAddress'),toEmail:g('toEmail'),toPhone:g('toPhone'),
invNumber:g('invNumber'),invDate:g('invDate'),dueDate:g('dueDate'),poNumber:g('poNumber'),
currency:g('currency'),taxPct:g('taxPct'),discountPct:g('discountPct'),
invNotes:g('invNotes'),invFooter:g('invFooter'),
bankName:g('bankName'),accName:g('accName'),accNumber:g('accNumber'),
sortCode:g('sortCode'),paypalEmail:g('paypalEmail'),
taxLabel:g('taxLabel'),accentColor:g('accentColor'),textColor:g('textColor'),
fontFamily:g('fontFamily'),
lines, logoDataURL, sigDataURL, currentTemplate, currentWatermark,
};
try { localStorage.setItem('sst_invoice_draft', JSON.stringify(draft)); toast('Draft saved!'); }
catch(e) { toast('Could not save draft'); }
}function loadDraft() {
try {
const raw = localStorage.getItem('sst_invoice_draft');
if (!raw) return;
const d = JSON.parse(raw);
const set = (id, val) => { const el=document.getElementById(id); if(el && val!==undefined) el.value=val; };
Object.entries(d).forEach(([k,v]) => { if (typeof v === 'string') set(k,v); });
if (d.lines) lines = d.lines;
if (d.logoDataURL) { logoDataURL=d.logoDataURL; document.getElementById('logoPreview').src=logoDataURL; document.getElementById('logoPreview').style.display='block'; document.getElementById('logoPlaceholder').style.display='none'; }
if (d.sigDataURL) sigDataURL=d.sigDataURL;
if (d.currentTemplate) setTemplate(d.currentTemplate, document.querySelector(`[data-tpl="${d.currentTemplate}"]`)||{classList:{add(){},remove(){}}});
if (d.currentWatermark) setWatermark(d.currentWatermark, document.querySelector(`[data-wm="${d.currentWatermark}"]`)||{className:''});
renderLines(); updatePreview();
toast('Draft restored!');
} catch(e) {}
}// ── MODE ──────────────────────────────────────────────────────
function setMode(mode) {
isPro = mode==='pro';
document.getElementById('btnSimple').classList.toggle('active',!isPro);
document.getElementById('btnPro').classList.toggle('active',isPro);
document.body.classList.toggle('pro-mode',isPro);
document.getElementById('proModeBar').style.display=isPro?'flex':'none';
if (isPro) initSigCanvas();
updatePreview();
try{localStorage.setItem('sst_inv_gen_mode',mode);}catch(e){}
}// ── UTILS ─────────────────────────────────────────────────────
function fmtNum(n) { return (Math.round(n*100)/100).toLocaleString('en',{minimumFractionDigits:2,maximumFractionDigits:2}); }
function escHtml(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>'); }
function escAttr(s) { return String(s||'').replace(/"/g,'"').replace(/'/g,'''); }
function toast(msg){const t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2800);}// ── INIT ──────────────────────────────────────────────────────
initDates();
renderLines();
updatePreview();
try{
const m=localStorage.getItem('sst_inv_gen_mode');
if(m==='pro'){setMode('pro');}
else { loadDraft(); }
}catch(e){}