Skip to content

Programmatic Mode ​

Programmatic Mode allows you to generate PDFs dynamically without rendering components to the screen. This guide explains how to use Programmatic Mode effectively.

Overview ​

Programmatic Mode is ideal when you want to:

  • Create PDFs from dynamic data
  • Generate multiple PDFs in batch
  • Create PDFs without visual rendering

Basic Usage ​

tsx
import React from "react";
import { useEasyPdf } from "@easypdf/react";

function ProgrammaticPDFGenerator() {
  const { createPDFBlob, downloadPDF, isLoading } = useEasyPdf();

  const generatePDF = async () => {
    const content = (
      <div>
        <h1>Generated PDF</h1>
        <p>This content is generated programmatically.</p>
      </div>
    );

    try {
      const blob = await createPDFBlob(content, {
        filename: "generated.pdf",
      });
      await downloadPDF(blob);
    } catch (error) {
      console.error("Failed to generate PDF:", error);
    }
  };

  return (
    <button onClick={generatePDF} disabled={isLoading}>
      {isLoading ? "Generating..." : "Generate PDF"}
    </button>
  );
}

Dynamic Content Generation ​

Data-Driven PDFs ​

tsx
function DataDrivenPDF() {
  const { createPDFBlob, downloadPDF } = useEasyPdf();

  const generateReport = async (data) => {
    const content = (
      <div>
        <h1>Data Report</h1>
        <table>
          <thead>
            <tr>
              <th>Name</th>
              <th>Value</th>
            </tr>
          </thead>
          <tbody>
            {data.map((item, index) => (
              <tr key={index}>
                <td>{item.name}</td>
                <td>{item.value}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );

    const blob = await createPDFBlob(content, {
      filename: "report.pdf",
    });
    await downloadPDF(blob);
  };

  return <button onClick={() => generateReport(data)}>Generate Report</button>;
}

Template-Based Generation ​

tsx
function PDFTemplate({ title, content, footer }) {
  return (
    <div className="pdf-template">
      <style>
        {`
          .pdf-template {
            font-family: Arial, sans-serif;
            padding: 20px;
          }
          .header {
            text-align: center;
            margin-bottom: 30px;
          }
          .content {
            margin: 20px 0;
          }
          .footer {
            margin-top: 30px;
            text-align: center;
            font-size: 12px;
          }
        `}
      </style>
      <div className="header">
        <h1>{title}</h1>
      </div>
      <div className="content">{content}</div>
      <div className="footer">{footer}</div>
    </div>
  );
}

function TemplateBasedGenerator() {
  const { createPDFBlob, downloadPDF } = useEasyPdf();

  const generateFromTemplate = async (data) => {
    const content = (
      <PDFTemplate
        title={data.title}
        content={data.content}
        footer={data.footer}
      />
    );

    const blob = await createPDFBlob(content);
    await downloadPDF(blob);
  };

  return (
    <button onClick={() => generateFromTemplate(data)}>Generate PDF</button>
  );
}

Batch Processing ​

Generate multiple PDFs in sequence:

tsx
function BatchPDFGenerator() {
  const { createPDFBlob } = useEasyPdf();

  const generateBatch = async (items) => {
    const blobs = await Promise.all(
      items.map(async (item) => {
        const content = (
          <div>
            <h1>{item.title}</h1>
            <p>{item.content}</p>
          </div>
        );

        return createPDFBlob(content, {
          filename: `${item.id}.pdf`,
        });
      })
    );

    return blobs;
  };

  return <button onClick={() => generateBatch(items)}>Generate Batch</button>;
}

Configuration ​

Hook-Level Configuration ​

tsx
const { createPDFBlob } = useEasyPdf({
  pageSize: "A4",
  margins: {
    top: 20,
    right: 20,
    bottom: 20,
    left: 20,
  },
  styles: {
    backgroundColor: "#ffffff",
    defaultFontSize: 12,
    defaultFontFamily: "Arial, sans-serif",
  },
});

Per-Generation Configuration ​

tsx
const blob = await createPDFBlob(content, {
  filename: "document.pdf",
  pageSize: "A4",
  watermark: {
    text: "CONFIDENTIAL",
    opacity: 0.2,
  },
  header: {
    text: "Company Report",
    fontSize: 12,
  },
  footer: {
    text: "Page {pageNumber} of {totalPages}",
    fontSize: 10,
  },
});

Error Handling ​

tsx
function PDFGeneratorWithErrorHandling() {
  const { createPDFBlob, downloadPDF } = useEasyPdf();
  const [error, setError] = useState(null);

  const generatePDF = async () => {
    try {
      setError(null);
      const blob = await createPDFBlob(content);
      await downloadPDF(blob);
    } catch (err) {
      setError(err.message);
      console.error("PDF generation failed:", err);
    }
  };

  return (
    <div>
      {error && <div className="error">{error}</div>}
      <button onClick={generatePDF}>Generate PDF</button>
    </div>
  );
}

Best Practices ​

  1. Performance

    • Optimize content before generation
    • Use batch processing for multiple PDFs
    • Implement proper error handling
  2. Content Structure

    • Use templates for consistent layouts
    • Separate content from presentation
    • Implement reusable components
  3. Error Handling

    • Implement proper error boundaries
    • Provide user feedback
    • Log errors appropriately
  4. Configuration

    • Use hook-level config for common settings shared across calls
    • Override specific settings per-call as needed
    • Validate configuration values

Examples ​

Invoice Generator ​

tsx
function InvoiceGenerator() {
  const { createPDFBlob, downloadPDF } = useEasyPdf();

  const generateInvoice = async (invoiceData) => {
    const content = (
      <div className="invoice">
        <style>
          {`
            .invoice {
              padding: 20px;
              font-family: Arial, sans-serif;
            }
            .invoice-header {
              text-align: center;
              margin-bottom: 30px;
            }
            .invoice-details {
              margin: 20px 0;
            }
            .invoice-table {
              width: 100%;
              border-collapse: collapse;
            }
            .invoice-table th,
            .invoice-table td {
              border: 1px solid #ddd;
              padding: 8px;
            }
          `}
        </style>

        <div className="invoice-header">
          <h1>Invoice #{invoiceData.id}</h1>
          <p>Date: {invoiceData.date}</p>
        </div>

        <div className="invoice-details">
          <h2>Bill To:</h2>
          <p>{invoiceData.customer.name}</p>
          <p>{invoiceData.customer.address}</p>
        </div>

        <table className="invoice-table">
          <thead>
            <tr>
              <th>Item</th>
              <th>Quantity</th>
              <th>Price</th>
              <th>Total</th>
            </tr>
          </thead>
          <tbody>
            {invoiceData.items.map((item, index) => (
              <tr key={index}>
                <td>{item.name}</td>
                <td>{item.quantity}</td>
                <td>${item.price}</td>
                <td>${item.quantity * item.price}</td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr>
              <td colSpan="3">Total</td>
              <td>${invoiceData.total}</td>
            </tr>
          </tfoot>
        </table>
      </div>
    );

    const blob = await createPDFBlob(content, {
      filename: `invoice-${invoiceData.id}.pdf`,
      watermark: {
        text: "PAID",
        opacity: 0.2,
      },
    });

    await downloadPDF(blob);
  };

  return (
    <button onClick={() => generateInvoice(data)}>Generate Invoice</button>
  );
}

Released under the MIT License.