HTL Tutorial #12: data-sly-unwrap - Removing HTML Wrappers
HTL Tutorial #12: data-sly-unwrap - Removing HTML Wrappers
What is data-sly-unwrap?
data-sly-unwrap removes the HTML element but keeps its content.
Useful for eliminating unnecessary wrappers in the final output.
Syntax
<element data-sly-unwrap>
Content kept
</element>Output:
Content keptThe <element> is removed!
Basic Example
<div data-sly-unwrap>
<p>This paragraph remains</p>
</div>Output:
<p>This paragraph remains</p>The <div> was removed, the <p> remains.
Why Use It?
1. Technical Wrappers in Edit Mode
In AEM, components often have wrappers for authoring that aren't needed in publish:
<div data-sly-unwrap="${wcmmode.disabled}">
<p>${properties.text}</p>
</div>In Edit Mode (wcmmode.disabled = false):
<div>
<p>Component text</p>
</div>In Publish Mode (wcmmode.disabled = true):
<p>Component text</p>The <div> wrapper is removed in publish!
2. Clean Markup
Avoid unnecessary <div> elements in the DOM:
<!-- Component -->
<div data-sly-unwrap="${!properties.showContainer}">
<h2>${properties.title}</h2>
<p>${properties.content}</p>
</div>If showContainer = false:
<h2>Title</h2>
<p>Content</p>If showContainer = true:
<div>
<h2>Title</h2>
<p>Content</p>
</div>3. Conditional Wrapper
<div data-sly-unwrap="${!properties.addWrapper}"
class="card">
<h3>${properties.title}</h3>
<p>${properties.text}</p>
</div>The author decides whether to add the .card wrapper or not!
Conditional data-sly-unwrap
data-sly-unwrap accepts a boolean expression:
<element data-sly-unwrap="${condition}">
Content
</element>- true: Removes element
- false: Keeps element
Example with WCM Mode
<!-- Wrapper only in edit mode -->
<sly data-sly-unwrap="${wcmmode.disabled}">
<div class="component-wrapper">
<div data-sly-resource="${'parsys' @ resourceType='wcm/foundation/components/parsys'}"></div>
</div>
</sly>Common Patterns
1. Conditional List
<ul data-sly-unwrap="${properties.inline}">
<li data-sly-list.item="${properties.items}">
${item}
</li>
</ul>If inline = false (normal list):
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>If inline = true (inline list, without <ul>):
<li>Item 1</li>
<li>Item 2</li>2. Wrapper Only for Multiple Elements
<div data-sly-unwrap="${itemList.count <= 1}">
<div data-sly-list.item="${properties.items}">
${item}
</div>
</div>1 element (unwrap):
<div>Single item</div>Multiple elements (keep wrapper):
<div>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>3. Conditional Semantic HTML
<section data-sly-unwrap="${!properties.isSection}">
<h2>${properties.title}</h2>
<p>${properties.content}</p>
</section>The author decides whether to use semantic <section> or just content.
The <sly> Element
HTL provides a special <sly> element that is always unwrapped automatically:
<sly>
<p>Content</p>
</sly>Output:
<p>Content</p><sly> is perfect for:
- Template definitions
- HTL logic without output
<!-- Template without wrapper -->
<sly data-sly-template.myTemplate="${@ text}">
<p>${text}</p>
</sly>
<!-- List without wrapper -->
<sly data-sly-list.item="${items}">
<div>${item}</div>
</sly>Difference Between <sly> and data-sly-unwrap
| Feature | <sly> |
data-sly-unwrap |
|---|---|---|
| Unwrap | Always | Conditional |
| Usage | Logical wrapper | Conditional wrapper |
| Expression | No | Yes (true/false) |
| Attributes | Not in output | Removed with element |
<!-- ALWAYS removed -->
<sly>
Content
</sly>
<!-- CONDITIONALLY removed -->
<div data-sly-unwrap="${condition}">
Content
</div>Complete Example: Card Component
<div data-sly-use.card="com.example.CardModel"
data-sly-unwrap="${card.unwrapContainer}"
class="card ${card.styleClass}">
<!-- Conditional header -->
<div data-sly-unwrap="${!card.hasHeader}" class="card-header">
<h3>${card.title}</h3>
</div>
<!-- Body -->
<div class="card-body">
<p>${card.content}</p>
</div>
<!-- Conditional footer -->
<div data-sly-unwrap="${!card.hasFooter}" class="card-footer">
<a href="${card.linkUrl}">${card.linkText}</a>
</div>
</div>Model:
public class CardModel {
@ValueMapValue
private String title;
@ValueMapValue
private String content;
@ValueMapValue
private String linkUrl;
@ValueMapValue
private String linkText;
@ValueMapValue
private boolean removeContainer;
@ValueMapValue
private String styleClass;
public boolean getHasHeader() {
return title != null && !title.isEmpty();
}
public boolean getHasFooter() {
return linkUrl != null && !linkUrl.isEmpty();
}
public boolean getUnwrapContainer() {
return removeContainer;
}
// Other getters...
}Output (with container, without footer):
<div class="card featured">
<div class="card-header">
<h3>Card Title</h3>
</div>
<div class="card-body">
<p>Card content</p>
</div>
</div>Output (without container, with footer):
<div class="card-header">
<h3>Card Title</h3>
</div>
<div class="card-body">
<p>Card content</p>
</div>
<div class="card-footer">
<a href="/link">Read more</a>
</div>Best Practices
- Use
<sly>for always-removed wrappers data-sly-unwrapfor conditions: dynamic wrappers- Clear naming: variables like
unwrapContainer,removeWrapper - Semantic HTML: Maintain semantic markup when possible
- Accessibility: Don't remove elements necessary for screen readers
- Testing: Test both cases (wrapped/unwrapped)
Combination with Other Statements
With data-sly-test
<!-- Show only if there's content, without wrapper -->
<div data-sly-test="${properties.content}"
data-sly-unwrap>
${properties.content}
</div>With data-sly-list
<div data-sly-unwrap="${properties.removeWrapper}">
<div data-sly-list.item="${properties.items}">
${item}
</div>
</div>With data-sly-element
<!-- Change tag OR remove completely -->
<div data-sly-element="${properties.tag}"
data-sly-unwrap="${!properties.tag}">
Content
</div>Common Mistakes
❌ Unwrap with necessary attributes
<!-- WRONG - loses the id! -->
<div id="important" data-sly-unwrap>
Content
</div>
<!-- CORRECT - unwrap only if id not needed -->
<div id="${properties.id}"
data-sly-unwrap="${!properties.id}">
Content
</div>❌ Forgetting condition
<!-- Always unwrapped (like <sly>) -->
<div data-sly-unwrap>
<!-- Conditional -->
<div data-sly-unwrap="${condition}">Practical Exercises
- Conditional Container: Component with optional wrapper
- Edit/Publish Mode: Wrapper only in edit
- List Wrapper: List with conditional
<ul> - Semantic Sections: Optional section tag
Next Lesson
In the next lesson we'll see data-sly-include and data-sly-resource for including other templates and components.
Lesson #12 of the HTL Tutorial series. ← Previous lesson | Next lesson →