AEM Guide

How Sling Resolves a Request to an AEM Component

How Sling turns a URL into a Resource and then into an AEM component through sling:resourceType, the /apps and /libs search paths, and why sling:resourceSuperType is the correct pattern for extending Core Components.

slingresourcetypecore-componentshtl

When AEM renders a page, what actually happens underneath is a two-step Sling resolution: first a URL becomes a Resource, then that Resource becomes a component — an HTL script or a servlet — that decides what HTML comes back. Understanding this pipeline explains why /apps can override /libs, why Core Components are extended with sling:resourceSuperType instead of being overwritten directly, and why a stray selector can break Dispatcher caching.

From URL to Resource

When a request like /content/mysite/en/home.html arrives, Sling splits the path into resource path, selectors, and extension, and uses the ResourceResolver to map /content/mysite/en/home to an actual JCR node. In an AEM project that node is usually a page (cq:Page), but what actually matters for rendering is its jcr:content node (or a child component’s), because that’s where the sling:resourceType property lives.

From Resource to component: sling:resourceType

sling:resourceType isn’t an absolute path — it’s a relative path Sling looks up across an ordered list of search paths. For example, if a component has:

sling:resourceType = "myproject/components/hero"

Sling looks for a script (.html, compiled from HTL to Java under the hood) or a servlet whose path, relative to one of the search paths, matches myproject/components/hero.

Sling’s search paths: why /apps comes before /libs

Sling’s default configuration (resource.resolver.searchpath) is ["/apps", "/libs"]. That means for the same sling:resourceType, Sling always tries whatever script it finds under /apps before the one it finds under /libs. This is the technical mechanism behind AEM’s classic “overlay”: if a product resource lives at /libs/something/component and you place a script at /apps/something/component, yours wins.

On AEM as a Cloud Service, /libs is fully immutable — you will never deploy anything there. This search-path overlay is still real for certain platform resources that still live under /libs (parts of Granite UI or the OSGi console, for instance), but it’s not the mechanism you should use to extend AEM Core Components — and that’s a common mistake.

Why Core Components aren’t overlaid: sling:resourceSuperType

The AEM Core Components (core/wcm/components/...) are shipped as a project dependency and end up installed under /apps, not /libs. Since they’re already in /apps, you can’t “beat” them by placing something under the same sling:resourceType in /apps without completely replacing the product component — losing any future improvement Adobe ships.

The correct pattern is component inheritance via sling:resourceSuperType:

<!-- /apps/myproject/components/text/.content.xml -->
<jcr:root
    jcr:primaryType="cq:Component"
    jcr:title="Text"
    sling:resourceSuperType="core/wcm/components/text/v2/text"/>

With this, your myproject/components/text component inherits everything you don’t explicitly override (dialog, HTL script, Sling Model) from the resourceSuperType. If your HTL script doesn’t define a given block, Sling walks up the resourceSuperType chain until it finds it on the Core Component. This is what lets you customize only the 10% you need to change without duplicating the other 90%.

Selectors and extension also change which script is picked

Selectors and the request extension (.html, .json, a selector like .print) also factor into resolution: Sling looks for a script specific to that combination first (text.print.html) before falling back to the default script (text.html). This is exactly what AEM’s JSON Exporter (.model.json) relies on to serve a component’s Sling Model representation without touching the normal render script.

Where this matters on a real AEM project