Pogodoc
Templates

EJS/HTML Templates

Create simple and efficient document templates using EJS templating or plain HTML

EJS/HTML Templates

EJS (Embedded JavaScript) and plain HTML templates provide a simple, straightforward way to create documents without the complexity of a JavaScript framework build process. Perfect for invoices, receipts, certificates, and other documents with straightforward layouts.

EJS templates are ideal for simple documents with variable substitution. For complex layouts with reusable components, consider using JavaScript Framework Templates.

Overview

EJS and HTML templates offer:

  • No build process required - Write and use immediately
  • Simple syntax - Easy to learn and maintain
  • Inline templates - Pass templates directly in API calls
  • Quick iteration - Fast development cycle

Template Types

EJS Templates

EJS (Embedded JavaScript) allows you to embed JavaScript expressions and logic directly in your HTML:

<h1>Hello, <%= customerName %>!</h1>
<% if (isPremium) { %>
<p>Thank you for being a premium member!</p>
<% } %>

Plain HTML Templates

Plain HTML with no templating logic, useful for static content:

<h1>Certificate of Completion</h1>
<p>This certifies that the holder has completed the course.</p>

Creating EJS Templates

Write Your Template

Create an HTML file with EJS syntax for dynamic content.

invoice-template.ejs
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Invoice</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      padding: 40px;
      color: #333;
    }
    .header {
      text-align: center;
      margin-bottom: 40px;
    }
    .invoice-details {
      margin-bottom: 30px;
    }
    table {
      width: 100%;
      border-collapse: collapse;
      margin: 20px 0;
    }
    th {
      background-color: #f4f4f4;
      padding: 12px;
      text-align: left;
      border-bottom: 2px solid #333;
    }
    td {
      padding: 10px 12px;
      border-bottom: 1px solid #ddd;
    }
    .total-row {
      font-weight: bold;
      font-size: 1.2em;
    }
    .footer {
      margin-top: 40px;
      text-align: center;
      color: #666;
      font-size: 0.9em;
    }
  </style>
</head>
<body>
  <div class="header">
    <h1>INVOICE</h1>
    <p>Invoice #<%= invoiceNumber %></p>
    <p>Date: <%= date %></p>
  </div>

  <div class="invoice-details">
    <h2>Bill To:</h2>
    <p><strong><%= customer.name %></strong></p>
    <p><%= customer.address %></p>
    <p><%= customer.city %>, <%= customer.state %> <%= customer.zip %></p>
  </div>

  <table>
    <thead>
      <tr>
        <th>Description</th>
        <th>Quantity</th>
        <th>Price</th>
        <th>Total</th>
      </tr>
    </thead>
    <tbody>
      <% items.forEach(function(item) { %>
        <tr>
          <td><%= item.description %></td>
          <td><%= item.quantity %></td>
          <td>$<%= item.price.toFixed(2) %></td>
          <td>$<%= (item.quantity * item.price).toFixed(2) %></td>
        </tr>
      <% }); %>
      <tr class="total-row">
        <td colspan="3" style="text-align: right;">Subtotal:</td>
        <td>$<%= subtotal.toFixed(2) %></td>
      </tr>
      <tr class="total-row">
        <td colspan="3" style="text-align: right;">Tax (<%= taxRate %>%):</td>
        <td>$<%= tax.toFixed(2) %></td>
      </tr>
      <tr class="total-row">
        <td colspan="3" style="text-align: right;">Total:</td>
        <td>$<%= total.toFixed(2) %></td>
      </tr>
    </tbody>
  </table>

  <div class="footer">
    <p>Thank you for your business!</p>
    <p><%= companyName %> | <%= companyEmail %> | <%= companyPhone %></p>
  </div>
</body>
</html>
receipt-template.ejs
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Receipt</title>
  <style>
    body {
      font-family: 'Courier New', monospace;
      max-width: 400px;
      margin: 0 auto;
      padding: 20px;
    }
    .receipt-header {
      text-align: center;
      border-bottom: 2px dashed #000;
      padding-bottom: 10px;
      margin-bottom: 20px;
    }
    .receipt-item {
      display: flex;
      justify-content: space-between;
      margin: 5px 0;
    }
    .total-section {
      border-top: 2px dashed #000;
      margin-top: 20px;
      padding-top: 10px;
    }
    .total {
      font-size: 1.2em;
      font-weight: bold;
    }
    .footer {
      text-align: center;
      margin-top: 20px;
      border-top: 2px dashed #000;
      padding-top: 10px;
      font-size: 0.9em;
    }
  </style>
</head>
<body>
  <div class="receipt-header">
    <h2><%= storeName %></h2>
    <p><%= storeAddress %></p>
    <p>Tel: <%= storePhone %></p>
    <p>Receipt #<%= receiptNumber %></p>
    <p><%= date %> <%= time %></p>
  </div>

  <div class="items">
    <% items.forEach(function(item) { %>
      <div class="receipt-item">
        <span><%= item.name %> x<%= item.quantity %></span>
        <span>$<%= (item.price * item.quantity).toFixed(2) %></span>
      </div>
    <% }); %>
  </div>

  <div class="total-section">
    <div class="receipt-item">
      <span>Subtotal:</span>
      <span>$<%= subtotal.toFixed(2) %></span>
    </div>
    <div class="receipt-item">
      <span>Tax:</span>
      <span>$<%= tax.toFixed(2) %></span>
    </div>
    <div class="receipt-item total">
      <span>TOTAL:</span>
      <span>$<%= total.toFixed(2) %></span>
    </div>
    <div class="receipt-item">
      <span>Payment Method:</span>
      <span><%= paymentMethod %></span>
    </div>
  </div>

  <div class="footer">
    <p>Thank you for your purchase!</p>
    <p>Please come again</p>
  </div>
</body>
</html>
certificate-template.ejs
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Certificate</title>
  <style>
    body {
      font-family: 'Georgia', serif;
      padding: 60px;
      text-align: center;
    }
    .certificate {
      border: 10px solid #0066cc;
      padding: 40px;
      box-shadow: 0 0 20px rgba(0,0,0,0.1);
    }
    .title {
      font-size: 3em;
      color: #0066cc;
      margin-bottom: 20px;
    }
    .subtitle {
      font-size: 1.5em;
      color: #666;
      margin-bottom: 40px;
    }
    .recipient {
      font-size: 2em;
      font-weight: bold;
      color: #333;
      margin: 30px 0;
      border-bottom: 2px solid #333;
      display: inline-block;
      padding-bottom: 10px;
    }
    .description {
      font-size: 1.2em;
      margin: 30px 0;
      line-height: 1.6;
    }
    .date-section {
      margin-top: 50px;
      font-size: 1em;
      color: #666;
    }
    .signature {
      margin-top: 60px;
      display: flex;
      justify-content: space-around;
    }
    .signature-line {
      border-top: 2px solid #333;
      width: 200px;
      padding-top: 10px;
    }
  </style>
</head>
<body>
  <div class="certificate">
    <div class="title">CERTIFICATE</div>
    <div class="subtitle">of <%= certificateType %></div>

    <p style="margin: 40px 0 20px 0;">This is to certify that</p>

    <div class="recipient"><%= recipientName %></div>

    <div class="description">
      has successfully completed<br>
      <strong><%= courseName %></strong><br>
      <%= description %>
    </div>

    <div class="date-section">
      <p>Awarded on <%= date %></p>
      <p>Certificate ID: <%= certificateId %></p>
    </div>

    <div class="signature">
      <div class="signature-line">
        <p><strong><%= instructorName %></strong></p>
        <p>Instructor</p>
      </div>
      <div class="signature-line">
        <p><strong><%= organizationName %></strong></p>
        <p>Organization</p>
      </div>
    </div>
  </div>
</body>
</html>

Pro Tip: For faster development, first create your template as a plain HTML with data filled. Then, replace your data with EJS placeholders.

Pro Tip: To preview how Pogodoc will render a PDF document you can use Ctrl/Cmd + P in your browser and preview how it would look like.

Use as Inline Template

You can pass EJS templates directly in the SDKs or our API.

Save as Template (Best Practice)

For templates you'll reuse, save them to your Pogodoc account.

Follow "Creating Templates". Or, save templates through the SDKs or our API.

EJS Syntax Reference

Variables

Output a variable value:

<h1>Hello, <%= name %>!</h1>

Unescaped Output

Output raw HTML (be careful with user input):

<div><%- htmlContent %></div>

Conditionals

<% if (isPremium) { %>
<p>Premium member benefits</p>
<% } else { %>
<p>Upgrade to premium</p>
<% } %>

Loops

<ul>
  <% items.forEach(function(item) { %>
  <li><%= item.name %> - $<%= item.price %></li>
  <% }); %>
</ul>

Functions

<% function formatPrice(price) { return '$' + price.toFixed(2); } %>
<p>Total: <%= formatPrice(total) %></p>

Comments

<%# This is a comment and won't appear in output %>

Best Practices

1. Use Inline Styles

CSS classes may not work reliably in PDFs. Always use inline styles or <style> tags:

<!-- ✅ Good -->
<div style="color: #333; font-size: 14px;">Content</div>

<style>
  .header {
    color: #333;
    font-size: 14px;
  }
</style>
<div class="header">Content</div>

<!-- ❌ Bad - external stylesheet -->
<link rel="stylesheet" href="styles.css" />

2. Include DOCTYPE and Charset

Always include proper HTML document structure:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Document Title</title>
  </head>
  <body>
    <!-- Content -->
  </body>
</html>

3. Use Absolute Units

Use pixels (px) or points (pt) instead of relative units:

<!-- ✅ Good -->
<div style="font-size: 14px; padding: 20px;">
  <!-- ❌ Bad -->
  <div style="font-size: 1em; padding: 2rem;"></div>
</div>

4. Validate Data

Always check if data exists before using it:

<% if (customer && customer.name) { %>
<p>Customer: <%= customer.name %></p>
<% } %>

5. Format Numbers

Use JavaScript functions to format numbers properly:

<!-- Currency -->
<p>Price: $<%= price.toFixed(2) %></p>

<!-- Percentage -->
<p>Discount: <%= (discount * 100).toFixed(0) %>%</p>

<!-- Thousands separator -->
<p>Total: $<%= total.toLocaleString('en-US', {minimumFractionDigits: 2}) %></p>

6. Handle Arrays Safely

Check if arrays exist and have items:

<% if (items && items.length > 0) { %>
<ul>
  <% items.forEach(function(item) { %>
  <li><%= item.name %></li>
  <% }); %>
</ul>
<% } else { %>
<p>No items found.</p>
<% } %>

Common Patterns

Tables with Totals

<table style="width: 100%; border-collapse: collapse;">
  <thead>
    <tr>
      <th
        style="text-align: left; padding: 10px; border-bottom: 2px solid #000;"
      >
        Item
      </th>
      <th
        style="text-align: right; padding: 10px; border-bottom: 2px solid #000;"
      >
        Price
      </th>
    </tr>
  </thead>
  <tbody>
    <% items.forEach(function(item) { %>
    <tr>
      <td style="padding: 8px;"><%= item.name %></td>
      <td style="text-align: right; padding: 8px;">
        $<%= item.price.toFixed(2) %>
      </td>
    </tr>
    <% }); %>
    <tr style="font-weight: bold; border-top: 2px solid #000;">
      <td style="padding: 8px;">Total:</td>
      <td style="text-align: right; padding: 8px;">$<%= total.toFixed(2) %></td>
    </tr>
  </tbody>
</table>

Conditional Styling

<div
  style="
  padding: 10px;
  background-color: <%= isPaid ? '#d4edda' : '#f8d7da' %>;
  color: <%= isPaid ? '#155724' : '#721c24' %>;
  border: 1px solid <%= isPaid ? '#c3e6cb' : '#f5c6cb' %>;
"
>
  Status: <%= isPaid ? 'Paid' : 'Unpaid' %>
</div>

Date Formatting

<% var date = new Date(); var formattedDate = date.toLocaleDateString('en-US', {
year: 'numeric', month: 'long', day: 'numeric' }); %>
<p>Date: <%= formattedDate %></p>

Multi-Page Documents

<div style="page-break-after: always;">
  <!-- First page content -->
  <h1>Page 1</h1>
  <p>Content for first page</p>
</div>

<div style="page-break-after: always;">
  <!-- Second page content -->
  <h1>Page 2</h1>
  <p>Content for second page</p>
</div>

<div>
  <!-- Last page (no page break) -->
  <h1>Page 3</h1>
  <p>Content for last page</p>
</div>

Troubleshooting

Template Not Rendering

  • Verify EJS syntax is correct (matching <% and %>)
  • Check for JavaScript errors in conditional logic
  • Ensure all variables exist in the data object
  • Test template syntax using an online EJS tester

Variables Not Showing

  • Confirm variable names match exactly (case-sensitive)
  • Check that data is being passed correctly
  • Use <%= JSON.stringify(data) %> to debug data structure

Styling Issues

  • Use inline styles or <style> tags only
  • Avoid external CSS files or frameworks
  • Use absolute units (px, pt) not relative (em, rem, %)
  • Test in a browser first before generating PDF

Layout Problems

  • Add <!DOCTYPE html> at the top
  • Set explicit widths and heights
  • Use box-sizing: border-box for consistent sizing
  • Add page-break-after: always for multi-page documents

Plain HTML Templates

For static documents without dynamic data, use plain HTML:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Certificate</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        padding: 60px;
        text-align: center;
      }
      .certificate {
        border: 5px solid #0066cc;
        padding: 40px;
      }
    </style>
  </head>
  <body>
    <div class="certificate">
      <h1>Certificate of Achievement</h1>
      <p>This certificate is awarded to</p>
      <h2 style="margin: 30px 0; color: #0066cc;">Outstanding Performance</h2>
      <p>Presented this day</p>
    </div>
  </body>
</html>

Examples

Next Steps