search
HTML star Featured

jsPDF Tutorial: Generate PDF in Browser Using HTML & JavaScript (Full Working Example)

Learn to create PDFs directly in the browser with jsPDF. Step-by-step guide with working examples for invoices, tickets, and styled documents.

person By Gautam Sharma
calendar_today December 31, 2024
schedule 16 min read
HTML JavaScript PDF jsPDF Frontend

Creating PDFs directly in the browser eliminates server processing, reduces costs, and gives users instant results. This tutorial shows you how to build a complete PDF generation system using jsPDF with real working examples.

What You’ll Build

By the end of this tutorial, you’ll have a working application that can:

  • Generate simple text PDFs
  • Create professionally styled multi-page documents
  • Build dynamic invoices with formatted tables
  • Generate event tickets from form data

All of this runs entirely in the browser with no server required.

Setting Up jsPDF

The fastest way to get started is using the CDN. Add this script tag to your HTML:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

Once loaded, jsPDF is available as window.jspdf.jsPDF.

Building the HTML Structure

Let’s create a clean interface with sections for each PDF example. We’ll use semantic HTML and organize everything in a container:

<div class="container">
  <h1>jsPDF Working Examples</h1>
  <p class="subtitle">Click any button to generate and download a PDF instantly</p>

  <div class="section">
    <h2>Example 1: Simple Text PDF</h2>
    <button onclick="generateSimplePDF()">Download Simple PDF</button>
  </div>

  <div class="section">
    <h2>Example 2: Styled Professional PDF</h2>
    <button onclick="generateStyledPDF()">Download Styled PDF</button>
  </div>

  <div class="section">
    <h2>Example 3: Invoice Generator</h2>
    <button onclick="generateInvoice()">Download Invoice</button>
    <button onclick="previewInvoice()">Preview Invoice in New Tab</button>
  </div>

  <div class="section">
    <h2>Example 4: Event Ticket Generator</h2>
    <form id="ticketForm" onsubmit="generateTicket(event)">
      <div class="form-grid">
        <input type="text" name="fullName" placeholder="Full Name" required>
        <input type="email" name="email" placeholder="Email" required>
        <input type="tel" name="phone" placeholder="Phone Number" required>
        <select name="eventType" required>
          <option value="">Select Event Type</option>
          <option value="conference">Conference</option>
          <option value="workshop">Workshop</option>
          <option value="webinar">Webinar</option>
        </select>
        <input type="date" name="eventDate" required>
      </div>
      <button type="submit">Generate & Download Ticket</button>
    </form>
  </div>
</div>

Each section contains either a button or form that triggers PDF generation. The form uses a grid layout for better organization on larger screens.

Styling with CSS

Professional styling makes the interface user-friendly and responsive:

body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  max-width: 1000px;
  margin: 0 auto;
  padding: 40px 20px;
  background: #f5f5f5;
}

.container {
  background: white;
  padding: 30px;
  border-radius: 10px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

.section {
  margin-bottom: 40px;
  padding: 20px;
  background: #f9f9f9;
  border-radius: 8px;
  border-left: 4px solid #3b82f6;
}

button {
  background: #3b82f6;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 15px;
  margin: 5px;
  transition: background 0.2s;
}

button:hover {
  background: #2563eb;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 15px;
}

@media (max-width: 600px) {
  .form-grid {
    grid-template-columns: 1fr;
  }
}

The CSS creates a card-based layout with a clean color scheme. The form grid adapts to mobile screens automatically.

JavaScript: Creating PDF Functions

Example 1: Simple Text PDF

This basic example shows the core jsPDF API:

function generateSimplePDF() {
  const doc = new jsPDF();
  doc.setFontSize(16);
  doc.text('Hello from jsPDF!', 20, 20);
  doc.setFontSize(12);
  doc.text('This PDF was generated directly in your browser.', 20, 35);
  doc.text('No server needed, completely client-side.', 20, 45);
  doc.save('simple-pdf.pdf');
}

The text() method takes three parameters: content, x-position, and y-position (in millimeters). The save() method triggers the browser download.

Example 2: Styled Professional PDF

Adding formatting, colors, and multiple pages:

function generateStyledPDF() {
  const doc = new jsPDF();

  doc.setFontSize(24);
  doc.setFont(undefined, 'bold');
  doc.text('Professional Document', 105, 25, { align: 'center' });

  doc.setFontSize(12);
  doc.setFont(undefined, 'italic');
  doc.setTextColor(100, 100, 100);
  doc.text('Generated with jsPDF', 105, 35, { align: 'center' });

  doc.setTextColor(0, 0, 0);
  doc.setFont(undefined, 'normal');
  doc.setFontSize(11);
  let y = 55;

  const content = [
    'This demonstrates various PDF features:',
    '',
    '• Multiple font sizes and styles',
    '• Text alignment and positioning',
    '• Colors and filled shapes',
    '• Multi-page documents'
  ];

  content.forEach(line => {
    doc.text(line, 20, y);
    y += 8;
  });

  y += 10;
  doc.setFillColor(59, 130, 246);
  doc.rect(20, y, 170, 20, 'F');
  doc.setTextColor(255, 255, 255);
  doc.setFontSize(12);
  doc.text('This is a colored box with white text', 105, y + 12, { align: 'center' });

  doc.addPage();
  doc.setTextColor(0, 0, 0);
  doc.setFontSize(18);
  doc.setFont(undefined, 'bold');
  doc.text('Page 2', 20, 25);
  doc.setFontSize(11);
  doc.setFont(undefined, 'normal');
  doc.text('jsPDF supports multiple pages seamlessly.', 20, 40);

  doc.save('styled-document.pdf');
}

Key features:

  • setFont() controls font weight (bold, italic, normal)
  • setTextColor() uses RGB values
  • rect() draws rectangles; the ‘F’ parameter fills them
  • addPage() creates new pages

Example 3: Dynamic Invoice Generator

Building a formatted invoice with tables and calculations:

function generateInvoice() {
  const doc = new jsPDF();
  let y = 20;

  doc.setFontSize(24);
  doc.setFont(undefined, 'bold');
  doc.text('ACME CORPORATION', 20, y);

  doc.setFontSize(10);
  doc.setFont(undefined, 'normal');
  y += 10;
  doc.text('123 Business Street', 20, y);
  y += 5;
  doc.text('New York, NY 10001', 20, y);
  y += 5;
  doc.text('contact@acme.com', 20, y);

  y += 15;
  doc.setFontSize(20);
  doc.setFont(undefined, 'bold');
  doc.text('INVOICE', 20, y);

  y += 12;
  doc.setFontSize(11);
  doc.setFont(undefined, 'normal');
  doc.text('Invoice #: INV-2024-001', 20, y);
  doc.text('Date: 2024-12-31', 150, y, { align: 'right' });
  y += 6;
  doc.text('Due Date: 2025-01-31', 150, y, { align: 'right' });

  y += 15;
  doc.setFont(undefined, 'bold');
  doc.text('BILL TO:', 20, y);
  y += 6;
  doc.setFont(undefined, 'normal');
  doc.text('John Doe', 20, y);
  y += 5;
  doc.text('john@example.com', 20, y);
  y += 5;
  doc.text('456 Client Street, Los Angeles, CA 90001', 20, y);

  y += 15;
  doc.setFillColor(240, 240, 240);
  doc.rect(20, y - 5, 170, 8, 'F');
  doc.setFont(undefined, 'bold');
  doc.text('Description', 22, y);
  doc.text('Qty', 115, y);
  doc.text('Price', 140, y);
  doc.text('Total', 170, y);

  y += 10;
  doc.setFont(undefined, 'normal');

  const items = [
    { desc: 'Web Development', qty: '40', price: '$75.00', total: '$3,000.00' },
    { desc: 'Logo Design', qty: '1', price: '$500.00', total: '$500.00' },
    { desc: 'Hosting (1 year)', qty: '1', price: '$120.00', total: '$120.00' }
  ];

  items.forEach((item, i) => {
    if (i % 2 === 0) {
      doc.setFillColor(250, 250, 250);
      doc.rect(20, y - 5, 170, 8, 'F');
    }
    doc.text(item.desc, 22, y);
    doc.text(item.qty, 115, y);
    doc.text(item.price, 140, y);
    doc.text(item.total, 170, y);
    y += 8;
  });

  y += 8;
  doc.line(115, y, 190, y);
  y += 8;
  doc.text('Subtotal:', 140, y);
  doc.text('$3,620.00', 170, y);
  y += 6;
  doc.text('Tax (8.5%):', 140, y);
  doc.text('$307.70', 170, y);
  y += 8;
  doc.setFont(undefined, 'bold');
  doc.setFontSize(12);
  doc.text('TOTAL:', 140, y);
  doc.text('$3,927.70', 170, y);

  const pageHeight = doc.internal.pageSize.height;
  doc.setFontSize(9);
  doc.setFont(undefined, 'italic');
  doc.text('Thank you for your business!', 105, pageHeight - 20, { align: 'center' });
  doc.text('Payment is due within 30 days', 105, pageHeight - 15, { align: 'center' });

  doc.save('invoice-INV-2024-001.pdf');
}

This function demonstrates:

  • Alternating row colors for better readability
  • Right-aligned text for numbers
  • Footer text positioned relative to page height
  • Professional invoice formatting

You can also add a preview function that opens the PDF in a new tab:

function previewInvoice() {
  const doc = new jsPDF();
  // ... same code as generateInvoice ...

  const pdfBlob = doc.output('blob');
  const url = URL.createObjectURL(pdfBlob);
  window.open(url, '_blank');
}

Example 4: Form Data to PDF Ticket

Converting user input into a styled event ticket:

function generateTicket(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
  const data = Object.fromEntries(formData.entries());

  const doc = new jsPDF();

  doc.setFillColor(245, 247, 250);
  doc.rect(0, 0, 210, 297, 'F');

  doc.setFillColor(59, 130, 246);
  doc.rect(0, 0, 210, 40, 'F');

  doc.setTextColor(255, 255, 255);
  doc.setFontSize(26);
  doc.setFont(undefined, 'bold');
  doc.text('EVENT TICKET', 105, 25, { align: 'center' });

  doc.setTextColor(0, 0, 0);

  const ticketNumber = 'TKT-' + Date.now().toString().slice(-8);
  doc.setFontSize(12);
  doc.setFont(undefined, 'normal');
  doc.text(`Ticket #${ticketNumber}`, 105, 50, { align: 'center' });

  doc.setFillColor(255, 255, 255);
  doc.roundedRect(20, 65, 170, 60, 3, 3, 'F');
  doc.setDrawColor(200, 200, 200);
  doc.roundedRect(20, 65, 170, 60, 3, 3, 'S');

  let y = 80;
  doc.setFontSize(11);
  doc.setFont(undefined, 'bold');
  doc.text('ATTENDEE INFORMATION', 25, y);

  y += 10;
  doc.setFont(undefined, 'normal');
  doc.text('Name:', 25, y);
  doc.text(data.fullName, 70, y);

  y += 8;
  doc.text('Email:', 25, y);
  doc.text(data.email, 70, y);

  y += 8;
  doc.text('Phone:', 25, y);
  doc.text(data.phone, 70, y);

  y = 140;
  doc.roundedRect(20, y, 170, 50, 3, 3, 'F');
  doc.roundedRect(20, y, 170, 50, 3, 3, 'S');

  y += 15;
  doc.setFont(undefined, 'bold');
  doc.text('EVENT DETAILS', 25, y);

  y += 10;
  doc.setFont(undefined, 'normal');
  doc.text('Type:', 25, y);
  doc.text(data.eventType.toUpperCase(), 70, y);

  y += 8;
  doc.text('Date:', 25, y);
  doc.text(new Date(data.eventDate).toLocaleDateString('en-US', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }), 70, y);

  y += 8;
  doc.text('Time:', 25, y);
  doc.text('9:00 AM - 5:00 PM', 70, y);

  y += 8;
  doc.text('Location:', 25, y);
  doc.text('Grand Convention Center, Hall A', 70, y);

  y = 210;
  doc.setFillColor(0, 0, 0);
  doc.rect(85, y, 40, 40, 'F');
  doc.setTextColor(100, 100, 100);
  doc.setFontSize(8);
  doc.text('Scan QR for entry', 105, y + 45, { align: 'center' });

  doc.setFontSize(9);
  doc.setTextColor(100, 100, 100);
  doc.text('Please bring this ticket to the event', 105, 270, { align: 'center' });
  doc.text('For questions, contact: support@events.com', 105, 275, { align: 'center' });

  doc.save(`ticket-${ticketNumber}.pdf`);

  event.target.reset();
  alert('Ticket downloaded successfully!');
}

This function shows:

  • Form data extraction using FormData API
  • Full-page background colors
  • Rounded rectangles with roundedRect()
  • Date formatting for better readability
  • Form reset after successful generation

Complete Working Example

Here’s the complete HTML file with all the code together. Copy this and save it as jspdf-demo.html to run locally:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>jsPDF Examples - Working Demo</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      max-width: 1000px;
      margin: 0 auto;
      padding: 40px 20px;
      background: #f5f5f5;
    }
    .container {
      background: white;
      padding: 30px;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    h1 {
      color: #333;
      margin-bottom: 10px;
    }
    .subtitle {
      color: #666;
      margin-bottom: 30px;
    }
    .section {
      margin-bottom: 40px;
      padding: 20px;
      background: #f9f9f9;
      border-radius: 8px;
      border-left: 4px solid #3b82f6;
    }
    h2 {
      color: #3b82f6;
      margin-top: 0;
    }
    button {
      background: #3b82f6;
      color: white;
      border: none;
      padding: 12px 24px;
      border-radius: 6px;
      cursor: pointer;
      font-size: 15px;
      margin: 5px;
      transition: background 0.2s;
    }
    button:hover {
      background: #2563eb;
    }
    input, select {
      width: 100%;
      padding: 10px;
      margin: 8px 0;
      border: 1px solid #ddd;
      border-radius: 4px;
      font-size: 14px;
      box-sizing: border-box;
    }
    .form-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 15px;
    }
    @media (max-width: 600px) {
      .form-grid {
        grid-template-columns: 1fr;
      }
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>jsPDF Working Examples</h1>
    <p class="subtitle">Click any button to generate and download a PDF instantly</p>

    <div class="section">
      <h2>Example 1: Simple Text PDF</h2>
      <button onclick="generateSimplePDF()">Download Simple PDF</button>
    </div>

    <div class="section">
      <h2>Example 2: Styled Professional PDF</h2>
      <button onclick="generateStyledPDF()">Download Styled PDF</button>
    </div>

    <div class="section">
      <h2>Example 3: Invoice Generator</h2>
      <button onclick="generateInvoice()">Download Invoice</button>
      <button onclick="previewInvoice()">Preview Invoice in New Tab</button>
    </div>

    <div class="section">
      <h2>Example 4: Event Ticket Generator</h2>
      <form id="ticketForm" onsubmit="generateTicket(event)">
        <div class="form-grid">
          <input type="text" name="fullName" placeholder="Full Name" required>
          <input type="email" name="email" placeholder="Email" required>
          <input type="tel" name="phone" placeholder="Phone Number" required>
          <select name="eventType" required>
            <option value="">Select Event Type</option>
            <option value="conference">Conference</option>
            <option value="workshop">Workshop</option>
            <option value="webinar">Webinar</option>
          </select>
          <input type="date" name="eventDate" required>
        </div>
        <button type="submit" style="margin-top: 15px;">Generate & Download Ticket</button>
      </form>
    </div>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
  <script>
    const { jsPDF } = window.jspdf;

    function generateSimplePDF() {
      const doc = new jsPDF();
      doc.setFontSize(16);
      doc.text('Hello from jsPDF!', 20, 20);
      doc.setFontSize(12);
      doc.text('This PDF was generated directly in your browser.', 20, 35);
      doc.text('No server needed, completely client-side.', 20, 45);
      doc.save('simple-pdf.pdf');
    }

    function generateStyledPDF() {
      const doc = new jsPDF();

      doc.setFontSize(24);
      doc.setFont(undefined, 'bold');
      doc.text('Professional Document', 105, 25, { align: 'center' });

      doc.setFontSize(12);
      doc.setFont(undefined, 'italic');
      doc.setTextColor(100, 100, 100);
      doc.text('Generated with jsPDF', 105, 35, { align: 'center' });

      doc.setTextColor(0, 0, 0);
      doc.setFont(undefined, 'normal');
      doc.setFontSize(11);
      let y = 55;

      const content = [
        'This demonstrates various PDF features:',
        '',
        '• Multiple font sizes and styles',
        '• Text alignment and positioning',
        '• Colors and filled shapes',
        '• Multi-page documents'
      ];

      content.forEach(line => {
        doc.text(line, 20, y);
        y += 8;
      });

      y += 10;
      doc.setFillColor(59, 130, 246);
      doc.rect(20, y, 170, 20, 'F');
      doc.setTextColor(255, 255, 255);
      doc.setFontSize(12);
      doc.text('This is a colored box with white text', 105, y + 12, { align: 'center' });

      doc.addPage();
      doc.setTextColor(0, 0, 0);
      doc.setFontSize(18);
      doc.setFont(undefined, 'bold');
      doc.text('Page 2', 20, 25);
      doc.setFontSize(11);
      doc.setFont(undefined, 'normal');
      doc.text('jsPDF supports multiple pages seamlessly.', 20, 40);

      doc.save('styled-document.pdf');
    }

    function generateInvoice() {
      const doc = new jsPDF();
      let y = 20;

      doc.setFontSize(24);
      doc.setFont(undefined, 'bold');
      doc.text('ACME CORPORATION', 20, y);

      doc.setFontSize(10);
      doc.setFont(undefined, 'normal');
      y += 10;
      doc.text('123 Business Street', 20, y);
      y += 5;
      doc.text('New York, NY 10001', 20, y);
      y += 5;
      doc.text('contact@acme.com', 20, y);

      y += 15;
      doc.setFontSize(20);
      doc.setFont(undefined, 'bold');
      doc.text('INVOICE', 20, y);

      y += 12;
      doc.setFontSize(11);
      doc.setFont(undefined, 'normal');
      doc.text('Invoice #: INV-2024-001', 20, y);
      doc.text('Date: 2024-12-31', 150, y, { align: 'right' });
      y += 6;
      doc.text('Due Date: 2025-01-31', 150, y, { align: 'right' });

      y += 15;
      doc.setFont(undefined, 'bold');
      doc.text('BILL TO:', 20, y);
      y += 6;
      doc.setFont(undefined, 'normal');
      doc.text('John Doe', 20, y);
      y += 5;
      doc.text('john@example.com', 20, y);
      y += 5;
      doc.text('456 Client Street, Los Angeles, CA 90001', 20, y);

      y += 15;
      doc.setFillColor(240, 240, 240);
      doc.rect(20, y - 5, 170, 8, 'F');
      doc.setFont(undefined, 'bold');
      doc.text('Description', 22, y);
      doc.text('Qty', 115, y);
      doc.text('Price', 140, y);
      doc.text('Total', 170, y);

      y += 10;
      doc.setFont(undefined, 'normal');

      const items = [
        { desc: 'Web Development', qty: '40', price: '$75.00', total: '$3,000.00' },
        { desc: 'Logo Design', qty: '1', price: '$500.00', total: '$500.00' },
        { desc: 'Hosting (1 year)', qty: '1', price: '$120.00', total: '$120.00' }
      ];

      items.forEach((item, i) => {
        if (i % 2 === 0) {
          doc.setFillColor(250, 250, 250);
          doc.rect(20, y - 5, 170, 8, 'F');
        }
        doc.text(item.desc, 22, y);
        doc.text(item.qty, 115, y);
        doc.text(item.price, 140, y);
        doc.text(item.total, 170, y);
        y += 8;
      });

      y += 8;
      doc.line(115, y, 190, y);
      y += 8;
      doc.text('Subtotal:', 140, y);
      doc.text('$3,620.00', 170, y);
      y += 6;
      doc.text('Tax (8.5%):', 140, y);
      doc.text('$307.70', 170, y);
      y += 8;
      doc.setFont(undefined, 'bold');
      doc.setFontSize(12);
      doc.text('TOTAL:', 140, y);
      doc.text('$3,927.70', 170, y);

      const pageHeight = doc.internal.pageSize.height;
      doc.setFontSize(9);
      doc.setFont(undefined, 'italic');
      doc.text('Thank you for your business!', 105, pageHeight - 20, { align: 'center' });
      doc.text('Payment is due within 30 days', 105, pageHeight - 15, { align: 'center' });

      doc.save('invoice-INV-2024-001.pdf');
    }

    function previewInvoice() {
      const doc = new jsPDF();
      let y = 20;

      doc.setFontSize(24);
      doc.setFont(undefined, 'bold');
      doc.text('ACME CORPORATION', 20, y);

      doc.setFontSize(10);
      doc.setFont(undefined, 'normal');
      y += 10;
      doc.text('123 Business Street', 20, y);
      y += 5;
      doc.text('New York, NY 10001', 20, y);
      y += 5;
      doc.text('contact@acme.com', 20, y);

      y += 15;
      doc.setFontSize(20);
      doc.setFont(undefined, 'bold');
      doc.text('INVOICE', 20, y);

      y += 12;
      doc.setFontSize(11);
      doc.setFont(undefined, 'normal');
      doc.text('Invoice #: INV-2024-001', 20, y);
      doc.text('Date: 2024-12-31', 150, y, { align: 'right' });
      y += 6;
      doc.text('Due Date: 2025-01-31', 150, y, { align: 'right' });

      y += 15;
      doc.setFont(undefined, 'bold');
      doc.text('BILL TO:', 20, y);
      y += 6;
      doc.setFont(undefined, 'normal');
      doc.text('John Doe', 20, y);
      y += 5;
      doc.text('john@example.com', 20, y);
      y += 5;
      doc.text('456 Client Street, Los Angeles, CA 90001', 20, y);

      y += 15;
      doc.setFillColor(240, 240, 240);
      doc.rect(20, y - 5, 170, 8, 'F');
      doc.setFont(undefined, 'bold');
      doc.text('Description', 22, y);
      doc.text('Qty', 115, y);
      doc.text('Price', 140, y);
      doc.text('Total', 170, y);

      y += 10;
      doc.setFont(undefined, 'normal');

      const items = [
        { desc: 'Web Development', qty: '40', price: '$75.00', total: '$3,000.00' },
        { desc: 'Logo Design', qty: '1', price: '$500.00', total: '$500.00' },
        { desc: 'Hosting (1 year)', qty: '1', price: '$120.00', total: '$120.00' }
      ];

      items.forEach((item, i) => {
        if (i % 2 === 0) {
          doc.setFillColor(250, 250, 250);
          doc.rect(20, y - 5, 170, 8, 'F');
        }
        doc.text(item.desc, 22, y);
        doc.text(item.qty, 115, y);
        doc.text(item.price, 140, y);
        doc.text(item.total, 170, y);
        y += 8;
      });

      y += 8;
      doc.line(115, y, 190, y);
      y += 8;
      doc.text('Subtotal:', 140, y);
      doc.text('$3,620.00', 170, y);
      y += 6;
      doc.text('Tax (8.5%):', 140, y);
      doc.text('$307.70', 170, y);
      y += 8;
      doc.setFont(undefined, 'bold');
      doc.setFontSize(12);
      doc.text('TOTAL:', 140, y);
      doc.text('$3,927.70', 170, y);

      const pageHeight = doc.internal.pageSize.height;
      doc.setFontSize(9);
      doc.setFont(undefined, 'italic');
      doc.text('Thank you for your business!', 105, pageHeight - 20, { align: 'center' });
      doc.text('Payment is due within 30 days', 105, pageHeight - 15, { align: 'center' });

      const pdfBlob = doc.output('blob');
      const url = URL.createObjectURL(pdfBlob);
      window.open(url, '_blank');
    }

    function generateTicket(event) {
      event.preventDefault();
      const formData = new FormData(event.target);
      const data = Object.fromEntries(formData.entries());

      const doc = new jsPDF();

      doc.setFillColor(245, 247, 250);
      doc.rect(0, 0, 210, 297, 'F');

      doc.setFillColor(59, 130, 246);
      doc.rect(0, 0, 210, 40, 'F');

      doc.setTextColor(255, 255, 255);
      doc.setFontSize(26);
      doc.setFont(undefined, 'bold');
      doc.text('EVENT TICKET', 105, 25, { align: 'center' });

      doc.setTextColor(0, 0, 0);

      const ticketNumber = 'TKT-' + Date.now().toString().slice(-8);
      doc.setFontSize(12);
      doc.setFont(undefined, 'normal');
      doc.text(`Ticket #${ticketNumber}`, 105, 50, { align: 'center' });

      doc.setFillColor(255, 255, 255);
      doc.roundedRect(20, 65, 170, 60, 3, 3, 'F');
      doc.setDrawColor(200, 200, 200);
      doc.roundedRect(20, 65, 170, 60, 3, 3, 'S');

      let y = 80;
      doc.setFontSize(11);
      doc.setFont(undefined, 'bold');
      doc.text('ATTENDEE INFORMATION', 25, y);

      y += 10;
      doc.setFont(undefined, 'normal');
      doc.text('Name:', 25, y);
      doc.text(data.fullName, 70, y);

      y += 8;
      doc.text('Email:', 25, y);
      doc.text(data.email, 70, y);

      y += 8;
      doc.text('Phone:', 25, y);
      doc.text(data.phone, 70, y);

      y = 140;
      doc.roundedRect(20, y, 170, 50, 3, 3, 'F');
      doc.roundedRect(20, y, 170, 50, 3, 3, 'S');

      y += 15;
      doc.setFont(undefined, 'bold');
      doc.text('EVENT DETAILS', 25, y);

      y += 10;
      doc.setFont(undefined, 'normal');
      doc.text('Type:', 25, y);
      doc.text(data.eventType.toUpperCase(), 70, y);

      y += 8;
      doc.text('Date:', 25, y);
      doc.text(new Date(data.eventDate).toLocaleDateString('en-US', {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      }), 70, y);

      y += 8;
      doc.text('Time:', 25, y);
      doc.text('9:00 AM - 5:00 PM', 70, y);

      y += 8;
      doc.text('Location:', 25, y);
      doc.text('Grand Convention Center, Hall A', 70, y);

      y = 210;
      doc.setFillColor(0, 0, 0);
      doc.rect(85, y, 40, 40, 'F');
      doc.setTextColor(100, 100, 100);
      doc.setFontSize(8);
      doc.text('Scan QR for entry', 105, y + 45, { align: 'center' });

      doc.setFontSize(9);
      doc.setTextColor(100, 100, 100);
      doc.text('Please bring this ticket to the event', 105, 270, { align: 'center' });
      doc.text('For questions, contact: support@events.com', 105, 275, { align: 'center' });

      doc.save(`ticket-${ticketNumber}.pdf`);

      event.target.reset();
      alert('Ticket downloaded successfully!');
    }
  </script>
</body>
</html>

Copy the entire HTML above and save it as jspdf-demo.html. Open it in any browser and all examples will work instantly.

Key Concepts Explained

PDF Coordinates System

jsPDF uses millimeters by default with the origin (0,0) at the top-left corner. An A4 page is 210mm wide and 297mm tall. Position text and shapes using x and y coordinates.

Color Management

Colors use RGB values from 0-255:

  • setTextColor(r, g, b) for text
  • setFillColor(r, g, b) for filled shapes
  • setDrawColor(r, g, b) for lines and borders

Font Styling

Available font styles: normal, bold, italic. Control with setFont(undefined, 'bold'). The first parameter is for custom fonts, use undefined for default.

Output Options

  • doc.save('filename.pdf') - Direct download
  • doc.output('blob') - Get blob for preview
  • doc.output('dataurlstring') - Get data URL

Browser Compatibility

jsPDF works in all modern browsers:

  • Chrome 60+
  • Firefox 55+
  • Safari 11+
  • Edge 79+

No polyfills or additional configuration needed.

Performance Considerations

For large PDFs with many pages:

  1. Avoid adding too many images or complex graphics
  2. Use doc.deletePage(pageNumber) to remove unwanted pages
  3. Generate PDFs on user action, not automatically on page load
  4. Consider showing a loading indicator for complex documents

Common Use Cases

This tutorial covers the most practical scenarios:

Business Documents: Invoices, receipts, quotes, and reports Event Management: Tickets, badges, and certificates Data Export: Converting tables and forms to printable PDFs User Downloads: Generating personalized documents on demand

Next Steps

Now that you understand the basics, you can:

  • Add logos and branding using doc.addImage()
  • Create headers and footers with doc.internal.pageSize
  • Build reusable PDF templates as JavaScript classes
  • Integrate with your backend for data fetching

The complete example above gives you a solid foundation for any PDF generation need in your web applications.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

HTML

How to Use pdf-lib in HTML: Create & Edit PDFs in Browser (Complete Guide)

Learn to create and edit PDFs directly in the browser with pdf-lib. Step-by-step guide with working examples for forms, watermarks, and PDF manipulation.

December 31, 2024
React

How to integrate jsPDF Library in React to Edit PDF in Browser

Quick guide to using jsPDF in React applications for creating and editing PDF documents directly in the browser.

December 29, 2024
JavaScript

Build PDFs Directly in the Browser: jsPDF vs pdf-lib vs PDF.js (Real Examples & Use Cases)

A practical comparison of jsPDF, pdf-lib, and PDF.js for browser-based PDF generation and manipulation. Learn which library fits your project with real code examples.

December 31, 2024