HomeFrontend Development › HTL vs JSX — translation guide

HTL vs JSX — translation guide

Purpose: Map every JSX habit to its HTL equivalent, and flag the habits that must change.

Who this page is for

AudienceWhy it matters to you
React engineersThe fastest way to become HTL-fluent

Direct translations

You write in JSXYou write in HTL
{title}${model.title}
{cond && <p>…</p>}<p data-sly-test="${model.cond}">…</p>
{items.map(i => <li key>…</li>)}<li data-sly-list.i="${model.items}">…</li>
<Child {...props} /><sly data-sly-resource="${'child' @ resourceType='…'}"></sly>
Reusable partial componentdata-sly-template + data-sly-call
className={clsx(…)}Ternaries in class attr, or map via data-sly-attribute
Fragment <>…</><sly>…</sly>

Habits that must change

React habitWhy it doesn't transferInstead
Logic in the template (.filter().sort() inline)HTL forbids arbitrary expressionsDo it in the Sling Model
Event handlers in markupHTL emits static HTMLSeparate JS in clientlibs, bind by class/data-attr
Component stateNo render-time state; one render per requestState is content (JCR) or client-side JS
Conditional early returnsTemplates are declarative onlydata-sly-test regions
Runtime data fetching in componentServer render is synchronous over contentModel fetches (sparingly); client JS for dynamic data

The mental switch

JSX components own behaviour and markup; HTL owns markup only. The triad is: Sling Model (logic) + HTL (markup) + clientlib JS (behaviour). It is the same separation you'd get extracting all logic from JSX into hooks and all interactivity into event modules — enforced by the platform.

Worked example

The PHI benefits list in React: a useBenefits() hook filters covered items, JSX maps them, onClick toggles detail. In AEM: PlanCardModel.getBenefits() returns view-ready rows; HTL lists them; plancard.js in the clientlib binds the toggle to .plan-card__row. Three files, one responsibility each.

Quick navigation