The value of this read-only custom field is calculated automatically using a custom text expression.
You can use it to display text that combines information from one or more fields (using field codes).
It also lets you create any custom text based on values from:
-
fields in the current work item, or
-
fields in other work items (for example: linked issues, sub-tasks, epics, stories, or issues selected via JQL).
Create a custom text formula field
Choose the template
Just like for any other formula field, follow the steps outlined here: Create and configure formula fields and select the Custom text formula field template in the field creation assistant.
Once selected click on Next.
Provide the expression
Creating an expression from scratch might feel like a challenge, especially if you are not familiar with our expression editor (known from our app Jira Workflow Toolbox). But we have you covered!
Either start by looking at the built-in example expressions or browse through the use cases and examples we have collected for you at the bottom of this page.
Preview the output
Once you have provided the expression you can preview and validate the output by selecting an existing work item.
Even if you don’t have the option to individually customize the display format (yet), you can choose to display emojis instead of just plain text just like in the example above RAG - due date traffic light.
📚 Use cases and examples
| Use case | Parsing mode | Expression |
|---|---|---|
| Components from all sub-tasks |
General |
%{distinct(fieldValue(%{issue.components}, subtasks()))}
|
| Earliest date and time from linked work items |
Jira expression |
let minDate = issue?.links.map(link => link.linkedIssue.customfield_nnnnn).filter( d => d != null); minDate.length > 0 ? new Date(minDate.reduce((a, b) => a < b ? a : b )).toString() : "" |
| Earliest date from linked work items |
Jira expression |
let minDate = issue?.links.map(link => link.linkedIssue.customfield_nnnnn).filter( d => d != null); minDate.length > 0 ? new CalendarDate(minDate.reduce((a, b) => a < b ? a : b )).toString() : "" |
| Field value validation |
General |
%{matches(%{issue.cfnnnnn},"6|7|8|9") ? "Valid":"Invalid"}
|
| First comment |
Jira Expression |
issue?.comments[0]?.body?.plainText == null ? '' : issue?.comments[0]?.body?.plainText |
| Historical due dates |
Jira expression |
issue.changelogs.filter(changelog => changelog.items.some(item
=> item.fieldId == 'duedate' && item.from != null)).map(
changelog => {duedateentry: changelog.items.filter(item =>
item.fieldId == 'duedate' )}).map(e => new CalendarDate(
e.duedateentry[0].from).toString()).join(", ")
|
| Keys of linked work items in current project |
General |
%{filterByProject(linkedIssues(), %{issue.project.key})}
|
| Keys of non-estimated sub-tasks |
Jira expression |
issue.subtasks.filter(s => s?.originalEstimate == null &&
s?.customfield_nnnnn == null).map(i => i.key).join(", ")
|
| Last status change date |
Jira expression |
issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'status')).map(changelog => changelog.created).concat(issue.created)[0].toString() |
| Previous reporter |
Jira expression |
let reporters= issue.changelogs.map(i=>i.items).flatten().filter(i=>i.field=="reporter");
reporters.length>0 ?
reporters.map(a=>a.fromString)[0] == null ?
""
:reporters.map(a=>a.fromString)[0]
: ""
|
| Project category |
General |
%{issue.project.category}
|
| Project lead |
General |
%{issue.project.leadDisplayName}
|
| RAG - due date traffic light |
General |
%{{issue.dueDate} = null or %{issue.resolution} != null? "⚪" :
(addDays({system.currentDateTime}, 14, RUN_AS_LOCAL) < {issue.dueDate} ? "🟢" :
(addDays({system.currentDateTime}, 7, RUN_AS_LOCAL) < {issue.dueDate} ? "🟡" :
({system.currentDateTime} < {issue.dueDate} ? "🟠" : "🔴")))}
|
| RAG - status traffic light |
General |
%{%{issue.status.category} = "To Do" ? "🔴⚪️⚪️" :
( %{issue.status.category} = "In Progress" ? "⚪️🟡⚪️" :
( %{issue.status.category} = "Done" ? "⚪️⚪️🟢" :
"⚪️⚪️⚪️" ))}
|
| Sub-task assignees |
General |
%{distinct(fieldValue(%{issue.assignee.displayName}, subtasks()))}
|
| Sub-task labels |
General |
%{distinct(fieldValue(%{issue.labels}, subtasks()))}
|
| Sub-task reporters |
General |
%{distinct(fieldValue(%{issue.reporter.displayName}, subtasks()))}
|
| Validate URLs for Google Analytics |
General |
%{count(findPattern(%{issue.cfnnnnn},
"\b(?:https?://|www\.)\S*(?=.*utm_source=)(?=.*utm_medium=)(?=.*utm_campaign=)\S\b"))
= count(findPattern(%{issue.cfnnnnn}, "\b(?:https?://|www\.)\S+\b")) ?
"🟢" : "🔴"}
|
| Visual progress of sub-tasks |
General |
let subtasks = issue.subtasks|| [];
if (subtasks.length == 0){ 'No sub-tasks' }
else{ let doneSubtasks = subtasks.filter(s => s.status.name ==
"Done");
let value = (doneSubtasks.length / subtasks.length) * 100;
let percent = value - value % 1 + (value % 1 >= 0.5 ? 1 : 0);
let totalBlocks = 10;
let filled = (percent / 100) * totalBlocks;
let full = filled - filled % 1 + (filled % 1 >= 0.5 ? 1 : 0);
let bar = '🟩'.repeat(full) + '⬜️'.repeat(totalBlocks - full);
`${bar} ${percent}%` }
|
| Visual progress of work item |
General |
%{%{issue.status} = "Open" ? "🟩🟩🟩🟩🟩" :
( %{issue.status} = "Planning" ? "🟩🟩⬜️⬜️⬜️" :
( %{issue.status} = "In Progress" ? "🟩🟩🟩⬜️⬜️" :
( %{issue.status} = "In Review" ? "🟩🟩🟩🟩⬜️" :
( %{issue.status} = "Done" ? "🟩🟩🟩🟩🟩" :
"⬜️⬜️⬜️⬜️⬜️" ))))}
|
| Work item ID |
General |
%{issue.id}
|