Day 15 of 40
HTL In Depth
Expressions, block statements, display context and XSS protection
By the end of today you should be able to
- Write HTL using the common block statements fluently
- Explain display context and how HTL protects against XSS
- Know when to use data-sly-resource, data-sly-include and data-sly-call
- Explain why business logic belongs in a Sling Model rather than in HTL
What HTL is and is not
HTL (HTML Template Language, formerly Sightly) is AEM's server-side templating language. It replaced JSP, and the replacement was motivated by two things: JSP let people put arbitrary Java in templates, and JSP made XSS easy. HTL fixes both by design — it is deliberately not a programming language. There are no loops you control, no arithmetic, no method calls with arguments. If you need logic, you write a Sling Model.
Everything is an HTML attribute, so a template is always valid HTML that a designer can open.
Expressions
<h1>${properties.jcr:title}</h1>
<h1>${model.title}</h1>
<p>${model.text || 'No text yet'}</p>
<a href="${model.link @ context='uri'}">Read more</a>
<p>${'Hello {0}, you have {1} messages' @ format=[user, count]}</p>
<p>${'Read more' @ i18n}</p>
Objects always available to you: properties (current resource's properties), pageProperties, resource, currentPage, request, wcmmode.
Block statements
data-sly-use— bind a Sling Model or Use-object to a name.data-sly-test— conditional. Assign the result to reuse it:data-sly-test.hasTitle="${model.title}".data-sly-list— iterate, exposingitemanditemList(withindex,count,first,last,odd,even).data-sly-repeat— likelist, but repeats the element itself rather than its children.data-sly-resource— render another resource through the full Sling pipeline.data-sly-include— include a script directly, with no resource resolution.data-sly-template/data-sly-call— define and call a reusable markup fragment.data-sly-element— set the element's tag name dynamically.data-sly-attribute— set attributes from a map.data-sly-unwrap— remove the host element, keeping its contents.
<div data-sly-use.model="com.adobe.aem.guides.wknd.core.models.Byline"
data-sly-test="${model.name}">
<h2>${model.name}</h2>
<ul data-sly-list.occupation="${model.occupations}">
<li class="${occupationList.first ? 'is-first' : ''}">${occupation}</li>
</ul>
</div>
data-sly-resource resolves a resource and renders it through Sling, so the target's own resource type, Sling Model and script apply. data-sly-include just pulls in a script file with the current context. Rendering another component is nearly always data-sly-resource.
Display context: the XSS protection
HTL escapes every expression automatically, and it picks the escaping strategy from where the
expression appears. Inside an element it uses HTML escaping; in an attribute, attribute escaping; in
an href, URI validation; inside a <script>, JavaScript escaping.
You can override it when you genuinely need to:
@ context='html'— allow a safe subset of HTML. This is what rich text fields need.@ context='uri'— validate as a URI.@ context='attribute','scriptString','styleToken','number'.@ context='unsafe'— no escaping at all.
It disables the protection HTL exists to provide, and it will be flagged in code review and by Cloud Manager's security scanning. If you are reaching for it to render rich text, you want context='html' instead — that permits formatting tags while still stripping scripts.
Templates and calls
<template data-sly-template.card="${@ title, image}">
<article class="card">
<img src="${image @ context='uri'}" alt="">
<h3>${title}</h3>
</article>
</template>
<div data-sly-call="${card @ title=model.title, image=model.imagePath}"></div>
Put shared templates in their own file and pull them in with data-sly-use to reuse across components.
The boundary
HTL's restrictions are the point. When you find yourself wanting a conditional chain, a computed value or string manipulation in a template, that is the signal to move it into a Sling Model getter — where it is testable, debuggable and reusable. Tomorrow is exactly that.
HTL renders; models decide. Escaping is automatic and context-aware — override it only with context='html' for rich text, effectively never with unsafe.
Watch
Adobe's own videos for this topic. They load only when you press play.
Read on Experience League
The primary sources these notes are drawn from.
Your notes
Saved automatically to this browser.
Check yourself
11 questions on today's material. 80% to pass.