AEM Insider
EP10 // AEM Complete Tutorial Series 2026

Expressions fill the markup.
These attributes build it.

data-sly-text · -set · -test · -list · -repeat · -attribute · -element
-resource · -include · -template · -call · -use · -unwrap
data-sly-*the element is the scopePart 2 of 2
aeminsider.com · @aeminsider · Not affiliated with Adobe Inc.
// Chapter 01 · What is a block statement?

It's just an attribute.

<p data-sly-text.identifier="${expression}">
data-sly-text

the statement

.identifier

a name you choose — optional

${expression}

what it works on

@ options

same options as Part 1

01

It sits on an element — and that element is its scope.

02

The attribute is removed from the output. Always.

03

<sly> is a tag that removes itself — a place to put a statement.

// Chapter 02 · Show it, or don't

Should this element exist at all?

hide it
<p data-sly-test="${properties.featured}">
    Featured
</p>

Falsy → the element and everything inside it is never written. Not hidden with CSS — view source, and the paragraph is not there at all.

name the result
<p data-sly-test.isFeatured="${properties.featured}">
    Featured
</p>

<p data-sly-test="${!isFeatured}">
    Not featured
</p>

HTL has no else. Name the answer, then test the opposite.

wcmmode

<p data-sly-test="${wcmmode.edit}">Authors only</p> — the element exists only inside the editor. Part 1 showed you the value. Here it does the work.

// Chapter 03 · Loops

One element, many times.

data-sly-list
<ul data-sly-list.tag="${properties.tags}">
    <li>${tagList.count}. ${tag}</li>
</ul>

Repeats what is inside the element. One <ul>, many <li>.

data-sly-repeat
<span data-sly-repeat.tag="${properties.tags}">
    ${tag}
</span>

Repeats the element itself. Many spans, no wrapper at all.

Every loop hands you a helper object — indexfrom 0 countfrom 1 firstmiddlelastoddeven
Named after your item: tag → tagList. No name given? item → itemList.
// Chapter 04 · Attributes and elements

Change the markup itself.

data-sly-attribute
<a data-sly-attribute.href="${properties.link}"
   data-sly-attribute.title="${properties.role}">
empty value→ the attribute is removed
true→ a bare boolean attribute
a map→ many attributes at once
data-sly-element
<h2 data-sly-element="${properties.headingLevel}">
    ${properties.name}
</h2>
h2 → h1 · h3the tag name comes from the dialog
one componentevery heading level a page needs
The two contexts owed from Part 1

attributeName checks a dynamic attribute name is safe. elementName checks a dynamic tag name against the allowed list. You don't type them — these two statements apply them for you. That is where they live.

// Chapter 05 · Composition

Pull something else in.

data-sly-include — another script, same resource
<sly data-sly-include="meta.html"></sly>

A file. Same node, same properties. The name resolves through the super-type chain — EP07.

data-sly-resource — another resource, full Sling resolution
<sly data-sly-resource="${'/content/…/htl-blocks/…/container/card'}"></sly>

A node. New resource, new properties. EP07's ladder runs again, from that node.

resourceType render a node as another component selectors pick a different script decorationTagName control the wrapper
// Chapter 06 · Reusable markup

Write it once. Call it anywhere.

01 DEFINE
<template data-sly-template.skillrow="${@ name, level}">
    <li>${name} — ${level}</li>
</template>   // renders nothing until it is called
02 CALL
<sly data-sly-call="${skillrow @ name='HTL', level='Confident'}"></sly>
03 LOAD
<sly data-sly-use.lib="lib.html"></sly>
<sly data-sly-call="${lib.skillrow @ name=…, level=…}"></sly>
data-sly-use also loads a Java class

A Sling Model — typed values instead of reaching into raw properties. That is the clean answer to everything we have worked around so far, and it is the next episode.

// Chapter 07 · Order on one element

They run in a fixed order.

three statements, one element
<ul data-sly-use.lib="lib.html"
    data-sly-test="${properties.featured}"
    data-sly-list.tag="${properties.tags}">
    <li>${tag}</li>
</ul>
data-sly-useruns first — its variables exist for the rest → data-sly-testruns before the loop — if it fails, nothing repeats → data-sly-listrepeats what survived
Not the order you typed them. Prefer one statement per element — and use <sly> when you need somewhere to put one.
// Chapter 08 · The block statements

The whole set.

Write
data-sly-textreplace the content
data-sly-unwrap · <sly>drop the element
data-sly-setgive a value a name
Decide
data-sly-testrender it, or don't
data-sly-test.namereuse the answer as the else
wcmmodeauthor-only markup
Repeat
data-sly-listthe content
data-sly-repeatthe element
itemListindex · count · first · last · odd · even
Build
data-sly-attributeattributes — removed when empty
data-sly-elementthe tag name
attributeName · elementNamevalidate dynamic names
Compose
data-sly-includeanother script, same resource
data-sly-resourceanother node, full Sling resolution
data-sly-template · -call · -usewrite once, call anywhere — or load a Sling Model
Expressions fill the markup. These build it. That is all of HTL.
// Next on AEM Insider

Next: Sling Models

data-sly-use can load a Java class. Typed values instead of raw properties — the @Model annotation, @ValueMapValue, adapting a resource. The end of every "fine for learning, not how real projects ship it" in this series.

EP11 · Sling Models