Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

[Jira 10.x] Images/icon and CSS styles not rendering in custom Velocity (.vm) email templates

Pratik Lodha
June 8, 2026

Hello Atlassian Community,

I am currently working on custom email notifications in Jira 10.3.5 using Velocity (.vm) templates. I am parsing some of the standard Jira email templates alongside my own custom wrappers, but I am running into rendering issues when the emails are actually delivered to the inbox.

Specifically, I am facing two distinct issues:

1. CSS Styles are Dropped or Ignored Any custom CSS classes I apply do not seem to render in the final email. Does Jira 10.x require strict inline CSS, or is there a specific standard stylesheet I should be parsing to inherit formatting?

2. Images are Broken or Blocked 

  • Check comment logo
  • Check user logo.

Please check the attached screenshots for reference showing the previously working version and the current failed version.

Any advice or best practices would be greatly appreciated!

Previous (v9.4.14)


Screenshot 2026-06-08 174842.png

Current ( v10.3.19

Screenshot 2026-06-08 174943.png

1 answer

0 votes
Artem Nek_Votazz
Atlassian Partner
June 8, 2026

Hi @Pratik Lodha 

A few things changed in Jira 10.0 around email/Velocity that could be behind
this — worth checking which one applies rather than assuming. Here's where I'd
look first.

The most likely candidate is the new Velocity allowlists introduced in Jira 10.0
(the main difference from 9.4.14):
• a file / file-type allowlist — custom .vm templates only render if allowlisted
(resource.loader.file.allowlist, file.resource.loader.filetype.allowlist)
• a method allowlist — method calls inside a template that aren't allowlisted get
blocked and logged

If that's what's happening, your custom wrappers and the helpers they call (the
CSS inliner and the image helpers) would be getting silently blocked — which would
fit both of your symptoms.

To confirm rather than guess, set:
atlassian.velocity.method.allowlist.debug=true
It keeps enforcement off but logs every blocked template/method, so the log names
the exact .vm files and calls involved. If you can run that and share what it logs
(or the related warnings in atlassian-jira.log), it'd point straight at the cause.

On your two questions:

1) CSS — mail clients strip <style>/class CSS, so it generally needs to be inlined.
Jira does this via Botocss, but it's opt-in: BotocssMailUtility#applyStyles (or
Botocss#inject), or inline styles directly. If you were relying on classes, that
could be why they drop — and if BotocssMailUtility isn't allowlisted on 10.x, the
inlining step may not run at all. Also worth knowing: the template structure
changed in 10.x — header.vm has different semantics now (actor details moved out
of the chrome), so #defaultMailHeader() is preferred over parsing header.vm
directly; footer.vm is unchanged.

2) Images — embedding logos as cid: (attached / Content-ID), or bundling the
graphics into the template archive, tends to be more reliable than URLs that need
a live Jira session (those often break in mail clients). It's also worth checking
whether the image macros are being caught by the method allowlist — same debug
flag.

If you can share a snippet of your custom wrapper plus whatever the debug log
shows, it'd be much easier to pin down which of these is actually biting you.

Docs:
• JIRA email template changes:
https://developer.atlassian.com/server/jira/platform/jira-email-template-changes/
• Velocity file allowlist:
https://developer.atlassian.com/server/framework/atlassian-sdk/velocity-file-allowlist/
• Velocity method allowlist:
https://developer.atlassian.com/server/framework/atlassian-sdk/configuring-the-velocity-allowlist/

Pratik Lodha
June 8, 2026

#disable_html_escaping()
#parse("templates/email/html/includes/emailconstants.vm")

#set ($issueLink = "")
#if ($issue)
#set ($issueType = $issue.getIssueType())
#set ($issueLink ="<a href='${baseurl}/browse/${issue.getKey()}'>#renderIssueTypeIcon(${issueType}) $xmlutils.escape($issue.getKey())</a>")
#end

#if ($author)
#set($actionerUser = $author)
#set ($authorLink = "#authorlinkname($author.name $linkstyle)")
#else
#set($actionerUser = $remoteUser)
#set ($authorLink = "#authorlinkname($remoteUser.name $linkstyle)")
#end

#set($headerTitle = $authorLink + "<strong>triggered</strong>" + " action ")
#parse("templates/email/html/includes/header.vm")

#set ($issueTitleBody="#parse('templates/email/html/includes/patterns/issue-title.vm')")
#rowWrapperNormal($issueTitleBody)

#rowWrapperNormalBegin('' 'wrapper-special-margin')
<table class="keyvalue-table">
<tr>
<th>Notification message:</th>
</tr>
<tr>
<td>$message</td>
</tr>
</table>

<hr>

#parse("com/nc/jira/custom/automation/rule/templates/custom-email-notification-fields-template.vm")

#rowWrapperNormalEnd()

#if($includeDescription)
#if ($issue.description)
#set($textParagraph = $issue.htmlDescription)
#rowWrapperNormal("#parse('templates/email/html/includes/patterns/text-paragraph.vm')", '', 'issue-description-container')
#end
#end

#set ($commentActionBody="#parse('templates/email/html/includes/patterns/comment-action.vm')")
#rowWrapperNormal($commentActionBody)
#parse("templates/email/html/includes/footer.vm")

Pratik Lodha
June 8, 2026

// Second File

#disable_html_escaping()
<table class="keyvalue-table">
#foreach($field in $templateFields)
#if($field.id == ("issuekey"))
#parse("templates/email/html/includes/fields/issuekey.vm")
#elseif($field.id == ("issuetype"))
#parse("templates/email/html/includes/fields/issuetype.vm")
#elseif($field.id == ("status"))
#parse("templates/email/html/includes/fields/status.vm")
#elseif($field.id == ("priority"))
#parse("templates/email/html/includes/fields/priority.vm")
#elseif($field.id == ("environment"))
#parse("templates/email/html/includes/fields/environment.vm")
#elseif($field.id == ("labels"))
#parse("templates/email/html/includes/fields/labels.vm")
#elseif($field.id == ("assignee"))
#parse("templates/email/html/includes/fields/assignee.vm")
#elseif($field.id == ("reporter"))
#parse("templates/email/html/includes/fields/reporter.vm")
#elseif($field.id == ("components"))
#parse("templates/email/html/includes/fields/components.vm")
#elseif($field.id == ("versions"))
#parse("templates/email/html/includes/fields/affectsversions.vm")
#elseif($field.id == ("fixVersions"))
#parse("templates/email/html/includes/fields/fixversions.vm")
#else
<tr>
<th>$field.name:</th>
<td>$field.value</td>
</tr>
#end
#end
</table>

 

Artem Nek_Votazz
Atlassian Partner
June 8, 2026

@Pratik Lodha Thank you for the logs. 

After digging into the 10.x specifics, I'd actually steer you away from the
Velocity allowlists as the cause on 10.3.5 — they probably aren't it here:

• The file allowlist only applies to .vm files loaded from the filesystem. If your
com/netcracker/... template is bundled inside the plugin JAR (which that path
suggests), the file allowlist doesn't touch it. If it's actually a loose file on
disk, then it would need allowlisting — worth confirming which it is.
• The method allowlist exists from 10.0, but on 10.3.x it runs in debug mode by
default: it logs non-allowlisted methods but doesn't block them. Enforcement
only turns on by default in 10.5. So on 10.3.5 your methods/macros are being
logged, not stripped.

So the more likely culprit is the template-structure + CSS changes between Jira 9
and 10. The stock templates and helper macros were reworked — header.vm has
different semantics now, and the chrome/row macros (#rowWrapperNormal, etc.) and
the email CSS changed. Your wrapper parses header.vm and leans on those macros and
on CSS classes (keyvalue-table, wrapper-special-margin, …), so if the stock pieces
you build on changed, both styling and layout would shift. Atlassian's migration
notes:
https://developer.atlassian.com/server/jira/platform/jira-email-template-changes/

On the two symptoms:
• CSS — email classes only show if Jira's CSS is inlined into the elements (mail
clients drop <style>/class CSS). That inlining is Botocss, and it's opt-in; if
the 9.x path that applied it changed, your classes come through bare and get
dropped.
• Icon — #renderIssueTypeIcon is what emits the issue-type icon, and how Jira
renders those has changed across versions, so that's the piece to inspect for
the broken image.

Quickest way to pin it: grab the raw HTML source of one delivered (broken) email
and check two things — are your classes still present but NOT converted into inline
style="…" (that's the CSS-inlining/Botocss issue), and what does the icon <img>
actually point at (a URL needing a Jira session vs a cid: attachment vs nothing).
Paste that and I can tell you which it is.

Pratik Lodha
June 8, 2026
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
        <base href="https://dev.server.com">
        <title>Message Title</title>
    </head>
    <body class="jira">
        <table id="background-table" cellpadding="0" cellspacing="0" width="100%">
                    <!-- header here -->
            <tr>
                <td id="header-pattern-container">
                    <table id="header-pattern" cellspacing="0" cellpadding="0" border="0">
                        <tr>
                            <td id="header-avatar-image-container" valign="top">
<img id="header-avatar-image" class="image_fix" src="$attachmentsManager.getAvatarUrl($actionerUser.name)" height="32" width="32" border="0" />
</td>
                            <td id="header-text-container" valign="middle">
<a class="user-hover" rel="jira.automation" id="email_jira.automation" href="https://dev.server.co/secure/ViewProfile.jspa" style="color:${textLinkColour};">JIRA Automation</a>
        <strong>triggered</strong> action 
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
                    <tr>
                <td id="email-content-container" >
                    <table id="email-content-table" cellspacing="0" cellpadding="0" border="0" width="100%">
                        <tr>
                            <!-- there needs to be content in the cell for it to render in some clients -->
                            <td class="email-content-rounded-top mobile-expand">&nbsp;</td>
                        </tr>
    
    
                
            
    <tr>
        <td  class="email-content-main mobile-expand ">
        
    
        <table class="page-title-pattern" cellspacing="0" cellpadding="0" border="0" width="100%">
            
                            <tr>
                    <td class="page-title-pattern-first-line ">
                        <a href=https://dev.server.com/browse/MSS'>MS SANDBOX</a> / <a href='https://dev.server.com/browse/MSS-123'><img src="https://dev.server.com/sd/issue-type-icons?icon=service-request" height="16" width="16" border="0" align="absmiddle" alt="Service Request"></a> <a href='https://dev.server.com/browse/MSS-123'>MSS-123</a>
                    </td>
                </tr>
                    <tr>
                <td style="vertical-align: top;" class="page-title-pattern-header-container">
                    <span class="page-title-pattern-header">
                       <a href='https://dev.server.com/browse/MSS-123'>MSS-123</a>
                    </span>
                </td>
            </tr>
        </table>
    
            </td>
    </tr>
    
        
        
    <tr>
        <td  class="email-content-main mobile-expand  wrapper-special-margin">
    <table class="keyvalue-table">
        <tr>
            <th>Notification message:</th>
        </tr>
        <tr>
            <td></td>
        </tr>
    </table>
    
    <hr>
    
    <table class="keyvalue-table">
                            <tr>
        <th>Issue Type:</th>
        <td class="has-icon">
                    <img src="https://dev.server.com/sd/issue-type-icons?icon=service-request" height="16" width="16" border="0" align="absmiddle" alt="Service Request">Service Request
        </td>
    </tr>      
</table>
    
    
    
        </td>
    </tr>
    
    
            
        
    <tr>
        <td  class="email-content-main mobile-expand ">
        
    
    <table id="actions-pattern" cellspacing="0" cellpadding="0" border="0" width="100%">
        <tr>
            <td id="actions-pattern-container" valign="middle">
                <table align="left">
                    <tr>
                        <td class="actions-pattern-action-icon-container">
                         <img class="actions-pattern-action-icon-image" src="$attachmentsManager.getImageUrl("/images/mail/comment-icon.png")" alt="${i18n.getText('jira.mentions.email.comment.add')}" title="${i18n.getText('jira.mentions.email.comment.add')}" height="16" width="16" border="0" />
                            </a>
                        </td>
                        <td class="actions-pattern-action-text-container">
                            <a href=https://dev.server.com/browse/MSS-364#add-comment" target="_blank" title="Add Comment">Add Comment</a>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
    
            </td>
    </tr>
    
    
      
                        <!-- there needs to be content in the cell for it to render in some clients -->
                        <tr>
                            <td class="email-content-rounded-bottom mobile-expand">&nbsp;</td>
                        </tr>
                    </table>
                </td>
            </tr>
    
            
                    <tr>
                <td id="footer-pattern">
                    <table id="footer-pattern-container" cellspacing="0" cellpadding="0" border="0">
                                            <tr>
                            <td id="footer-pattern-text" class="mobile-resize-text" width="100%">
                                This message was sent by Atlassian Jira
    
                                                                                                                    
                                                            
                                <span id="footer-build-information">(v10.3.19#10030019-<span title='95508b3a08ac0f09fbef8676c203b44bdc6e1a12' data-commit-id='95508b3a08ac0f09fbef8676c203b44bdc6e1a12}'>sha1:95508b3</span>)</span>
                                                        </td>
                            <td id="footer-pattern-logo-desktop-container" valign="top">
                                <table>
                                    <tr>
                                        <td id="footer-pattern-logo-desktop-padding">
                                            <img id="footer-pattern-logo-desktop"
                                                 src="https://dev.server.com/images/mail/atlassian-email-logo.png"
                                                 alt="Atlassian logo"
                                                 title="Atlassian logo"
                                                 width="191"
                                                 height="24"
                                                 class="image_fix"  />
                                        </td>
                                    </tr>
                                </table>
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </body>
    </html>

Suggest an answer

Log in or Sign up to answer