Learn how Liquid templating language simplifies dynamic document generation from structured data. Built for developers who need reliable, maintainable document automation.
Traditional document generation involves complex libraries, format-specific code, and maintenance overhead. Each document type requires different implementation approaches, creating technical debt and development complexity.
Liquid is a safe, designer-friendly templating language originally created by Shopify. It separates data from presentation, making documents maintainable by non-developers while remaining powerful enough for complex logic.
<!-- Simple variable substitution -->
<h1>Invoice {{ invoice.number }}</h1>
<p>Date: {{ invoice.date | date: "%B %d, %Y" }}</p>
<!-- Conditional content -->
{% if invoice.discount > 0 %}
<p>Discount Applied: {{ invoice.discount }}%</p>
{% endif %}
<!-- Loops for dynamic content -->
{% for item in invoice.items %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.quantity }}</td>
<td>${{ item.price | number }}</td>
</tr>
{% endfor %}
<!-- Built-in filters for formatting -->
<p>Total: ${{ invoice.total | number }}</p>
#Send structured data with template
curl --request POST \
--url https://dash.liquidtemplater.com/items/template_request \
--header 'Authorization: Bearer YOUR_BEARER_API_KEY_HERE_1234578' \
--header 'content-type: application/json' \
--data '{
"data": {
"invoice": {
"number": "INV-2025-0847",
"date": "2025-06-20",
"discount": 10,
"items": [
{"name": "Web Development", "quantity": 40, "price": 125},
{"name": "Design Services", "quantity": 20, "price": 95}
],
"total": 6900
}
},
"template": "<!-- Template content above -->",
"type": "pdf"
}'
The same template works across PDF, DOCX, and email formats, eliminating format-specific code.
Filters for Data Transformation
#Date formatting
{{ order.created_at | date: "%B %d, %Y at %I:%M %p" }}
#Number formatting
{{ price | money }} #$1,234.56
{{ quantity | pluralize: "item", "items" }} #1 item / 5 items
#String manipulation
{{ customer.name | upcase }} #JOHN SMITH
{{ description | truncate: 100 }} #Truncate long text
Complex Logic and Calculations
#Calculate totals within templates
{% assign subtotal = 0 %}
{% for item in order.items %}
{% assign line_total = item.quantity | times: item.price %}
{% assign subtotal = subtotal | plus: line_total %}
{% endfor %}
#Conditional sections based on data
{% case customer.tier %}
{% when "premium" %}
<p>Thank you for being a Premium customer!</p>
{% when "enterprise" %}
<p>Your dedicated account manager: {{ customer.manager }}</p>
{% else %}
<p>Upgrade to Premium for additional benefits.</p>
{% endcase %}
Invoice Template
<!DOCTYPE html>
<html>
<head>
<style>
.header { color: #333; border-bottom: 2px solid #ddd; }
.total { font-weight: bold; font-size: 1.2em; }
</style>
</head>
<body>
<div class="header">
<h1>{{ company.name }}</h1>
<p>{{ company.address }}</p>
</div>
<h2>Invoice {{ invoice.number }}</h2>
<table>
<tr><th>Description</th><th>Qty</th><th>Rate</th><th>Total</th></tr>
{% for item in invoice.items %}
<tr>
<td>{{ item.description }}</td>
<td>{{ item.quantity }}</td>
<td>${{ item.rate | number }}</td>
<td>${{ item.quantity | times: item.rate | number }}</td>
</tr>
{% endfor %}
</table>
<div class="total">
Total: ${{ invoice.total | number }}
</div>
</body>
</html>
Report Template
#Monthly Performance Report
##Summary
- Revenue: ${{ report.revenue | number }}
- Growth: {{ report.growth_rate }}%
- Active Users: {{ report.active_users | number }}
##Top Performing Features
{% assign sorted_features = report.features | sort: "usage" | reverse %}
{% for feature in sorted_features limit: 5 %}
{{ forloop.index }}. {{ feature.name }} - {{ feature.usage }}% adoption
{% endfor %}
##Regional Breakdown
{% for region in report.regions %}
###{{ region.name }}
Revenue: ${{ region.revenue | number }}
Users: {{ region.users | number }}
{% endfor %}
One Template, Multiple Formats
//Generate different formats from same template
async function generateDocument(data, format) {
const template = `
<h1>{{ document.title }}</h1>
<p>{{ document.content }}</p>
<ul>
{% for item in document.items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
`;
const response = await fetch('https://dash.liquidtemplater.com/items/template_request', {
method: 'POST',
headers: {
'Authorization': `Bearer ${YOUR_BEARER_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: data,
template: template,
type: format // 'pdf', 'docx', or 'email'
})
});
const { data: { id } } = await response.json();
const result = await pollForCompletion(id);
return `https://dash.liquidtemplater.com/assets/${result.data.result}?download=true`;
}
Template Composition
#Include common headers
{% include 'company_header' %}
#Document-specific content
<h2>{{ document.type | capitalize }} #{{ document.number }}</h2>
#Include common footers
{% include 'company_footer' %}
Template Validation
#Use default values for missing data
{{ customer.name | default: "Valued Customer" }}
#Check for data existence
{% if order.shipping_address %}
<h3>Shipping Address</h3>
<p>{{ order.shipping_address }}</p>
{% endif %}
#Debug output during development
{{ debug_data | json }}
Common Template Patterns
#Safe number formatting
${{ price | default: 0 | number }}
#Handle empty arrays
{% if items.size > 0 %}
{% for item in items %}
<p>{{ item.name }}</p>
{% endfor %}
{% else %}
<p>No items found.</p>
{% endif %}
#Nested object access
{{ user.profile.address.city | default: "Not specified" }}
Template Efficiency
Data Structure Optimization
From String Concatenation
//Old approach
const html = `<h1>${invoice.number}</h1><p>Total: ${invoice.total}</p>`;
//Liquid template approach
const template = `<h1>{{ invoice.number }}</h1><p>Total: ${{ invoice.total | number }}</p>`;
From Complex Template Engines Liquid provides the right balance of power and simplicity, avoiding the complexity of full programming languages in templates while offering more functionality than basic string replacement.
Built by developers, for developers. No vendor lock-in, no complex setup.