- Built-in Functions
- Advanced Features
- JQL Templates
- Best Practices
- Display Condition Templates
- Performance Tips
- Debugging Templates
- Common Mistakes
- Next Steps
Advanced template features including functions, JQL templates, display conditions, and debugging.
Built-in Functions
Functions are called directly (not with pipe):
Date Functions
| Function | Description | Example |
|---|---|---|
now() |
Current date/time | {{ now() }} |
date(value) |
Parse date | {{ date("2024-01-15") }} |
isPast(date) |
Check if date is in past | {% if isPast(duedate) %}Overdue{% endif %} |
isFuture(date) |
Check if date is in future | {% if isFuture(duedate) %}On time{% endif %} |
dateBefore(d1, d2) |
Check if d1 is before d2 | {% if dateBefore(created, updated) %}...{% endif %} |
dateAfter(d1, d2) |
Check if d1 is after d2 | {% if dateAfter(updated, created) %}Modified{% endif %} |
dateDiff(d1, d2, unit) |
Difference between dates | {{ dateDiff(now(), created, "days") }} days ago |
dateDiff units: milliseconds, seconds, minutes, hours, days, weeks
Utility Functions
| Function | Description | Example |
|---|---|---|
range(start, end, step) |
Generate number array | {% for i in range(1, 5) %}{{ i }}{% endfor %} |
range() example - progress bar:
{% set progress = 75 %}
{% set filled = (progress / 5) | round %}
{% for i in range(1, 20) %}{% if i <= filled %}#{% else %}.{% endif %}{% endfor %}
Advanced Features
Set Variables
{% set count = 0 %}
{% set userName = issue.fields.assignee.displayName %}
{% set total = linkedIssues | len %}
With Blocks
{% with issue.fields.assignee as user %}
{% if user %}
{{ user.displayName }} ({{ user.emailAddress }})
{% endif %}
{% endwith %}
Or assignment syntax:
{% with count = linkedIssues | len %}
Total: {{ count }}
{% endwith %}
Switch/Case
{% switch issue.fields.status.name %}
{% case "To Do" %}
Not started
{% case "In Progress", "In Review" %}
Work in progress
{% case "Done" %}
Completed
{% default %}
Other
{% endswitch %}
Ternary Operator
{{ issue.fields.priority ? issue.fields.priority.name : "None" }}
Whitespace Control
Use - to trim whitespace:
{%- if condition -%}
Content
{%- endif -%}
JQL Templates
Use templates in JQL queries:
Issues in Same Epic
"Epic Link" = {{ issue.key }} AND status != Done ORDER BY priority DESC
Same Project and Type
project = {{ issue.fields.project.id }} AND type = {{ issue.fields.issuetype.id }} AND key != {{ issue.key }}
Assigned to Current User
assignee = {{ currentUser.accountId }} AND status in ("To Do", "In Progress") ORDER BY priority DESC
Issues Blocking This One
issue in linkedIssues({{ issue.key }}, "is blocked by") AND status != Done
Same Fix Version
{% if issue.fields.fixVersions | length > 0 %}
fixVersion in ({% for version in issue.fields.fixVersions %}{{ version.id }}{% if not loop.last %},{% endif %}{% endfor %}) AND type = Bug AND status != Closed
{% endif %}
Same Components
{% if issue.fields.components | length > 0 %}
component in ({% for comp in issue.fields.components %}"{{ comp.name }}"{% if not loop.last %},{% endif %}{% endfor %}) AND key != {{ issue.key }}
{% endif %}
Best Practices
Always Check for Null
{% if issue.fields.assignee %}
{{ issue.fields.assignee.displayName }}
{% endif %}
Or use default filter:
{{ issue.fields.assignee.displayName | default("Unassigned") }}
Safe Property Access
For deeply nested properties:
{% with issue.fields.customfield_10001 as cf %}
{% if cf and cf.value %}
{{ cf.value }}
{% endif %}
{% endwith %}
Comments
{# This won't be displayed #}
{% comment %}
Multi-line comment
{% endcomment %}
HTML Escaping
{{ value }}- Auto-escapes HTML (safe){{{ value }}}- Raw output (use carefully)
Display Condition Templates
Display Conditions use the same template syntax but with special output interpretation rules. They control when a panel is shown.
Output Interpretation
| Output | Result |
|---|---|
Empty string "" |
Hidden |
false (boolean) |
Hidden |
String "false", "no", "0" |
Hidden |
| Any other value | Shown |
Simple Expression Examples
{{ 'urgent' in fields.labels }}
→ Outputs true or false
{{ fields.priority.name == 'High' }}
→ Outputs true or false
{{ fields.assignee }}
→ Outputs user object (truthy) or empty (falsy)
If Statement Examples
{% if fields.assignee %}show{% endif %}
→ Outputs "show" or ""
{% if 'blocker' in fields.labels %}yes{% endif %}
→ Outputs "yes" or ""
Complex Expressions
Multiple conditions (AND):
{{ fields.priority.name == 'High' and fields.status.name != 'Done' }}
Multiple conditions (OR):
{{ 'urgent' in fields.labels or 'critical' in fields.labels }}
Using filters:
{% set highPriorityBlockers = linkedIssues | filter("fields.priority.name", "==", "High") %}
{{ highPriorityBlockers | len > 0 }}
Current User Conditions
{{ fields.assignee.accountId == currentUser.accountId }}
→ Show only to assignee
{{ fields.reporter.accountId == currentUser.accountId }}
→ Show only to reporter
{% if fields.assignee.accountId == currentUser.accountId or fields.reporter.accountId == currentUser.accountId %}show{% endif %}
→ Show to assignee OR reporter
Error Safety
Display Conditions use fail-close behavior:
- Syntax error → panel hidden
- Missing data → panel hidden
- Exception → panel hidden
Always test your conditions before deploying.
Safe null checks:
{% if fields.assignee and fields.assignee.accountId == currentUser.accountId %}show{% endif %}
Performance Tips
The template engine has a 10,000 iteration limit and an 800ms timeout. Keep these limits in mind when writing complex templates.
Avoid Heavy Operations in Loops
Slow:
{% for issue in linkedIssues %}
{% set filtered = linkedIssues | filter("fields.status.name", "==", issue.fields.status.name) %}
{{ issue.key }}: {{ filtered | len }} similar
{% endfor %}
Faster - pre-compute outside the loop:
{% set doneCount = linkedIssues | filter("fields.status.statusCategory.key", "==", "done") | len %}
{% set total = linkedIssues | len %}
Done: {{ doneCount }} / {{ total }}
Limit Loop Iterations
Use | take(N) to limit the number of items processed:
{% for issue in linkedIssues | take(10) %}
{{ issue.key }}
{% endfor %}
{% if linkedIssues | len > 10 %}
...and {{ linkedIssues | len - 10 }} more
{% endif %}
Use {% set %} to Cache Values
Avoid repeating expensive expressions:
{% set assigneeName = issue.fields.assignee.displayName | default("Unassigned") %}
{{ assigneeName }} has {{ linkedIssues | len }} related issues.
Reminder: {{ assigneeName }} should review these.
Keep Conditional Logic Simple
Deeply nested conditions are harder to debug and slower to execute. Consider using switch for multiple conditions on the same value.
See Limits for all template engine constraints.
Debugging Templates
When templates don’t work as expected:
- Check variable paths - Use
{{ issue | json(2) }}to see available data - Test conditions - Output intermediate values:
Debug: {{ issue.fields.status.name }} - Verify filters - Test filter chain step by step
- Check for null - Many errors come from accessing properties on null values
Common Mistakes
Accessing Null Properties
Wrong:
{{ issue.fields.assignee.displayName }}
Right:
{{ issue.fields.assignee.displayName | default("Unassigned") }}
{# or #}
{% if issue.fields.assignee %}
{{ issue.fields.assignee.displayName }}
{% endif %}
Date Comparison
Wrong:
{% if issue.fields.duedate < now %}
Right:
{% if isPast(issue.fields.duedate) %}
{# or #}
{% if dateBefore(issue.fields.duedate, now()) %}
Building Dynamic Arrays
Wrong (JavaScript methods don’t work):
{% set items = [] %}
{% set items = items.concat([newItem]) %}
Right (use filter instead):
{% set doneItems = allItems | filter("status", "==", "Done") %}
Unsupported Filters
| Not Supported | Use Instead |
|---|---|
values |
\| map("fieldName") on array |
sum |
Manual loop with counter |
keys |
\| map("key") if applicable |
groupBy |
Use \| uniq + \| filter() |
Next Steps
- Templates - Basic syntax and variables
- Template Filters - All available filters
- Message Formats - HTML, Markdown, ADF
- Limits - Template engine constraints and performance tips
Message Field for Jira Cloud