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.
<!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><!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><!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.
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-boxfor consistent sizing - Add
page-break-after: alwaysfor 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
Invoice Template
Complete EJS invoice with line items and calculations
Receipt Template
Simple receipt template with totals
Report Template
Multi-page report with tables and charts
Certificate Template
Static HTML certificate template