Sling Models
Understand Sling Models — the backbone of AEM component logic. Learn how they work, how to write them, and the gotchas that trip up every developer.
Last reviewed: December 2024
🔍 What Is It?
A Sling Model is a Java POJO (Plain Old Java Object) annotated with @Model. It:
- →Adapts a
ResourceorSlingHttpServletRequestto a typed Java object - →Automatically injects JCR property values using
@ValueMapValue - →Can inject OSGi services using
@OSGiService - →Can inject child resources, parent pages, and request parameters
- →Is used by HTL templates via
data-sly-use
Sling Models replaced the older WCMUsePojo and are now the standard way to write AEM component logic.
🎯 Why Do We Need It?
HTL is intentionally limited — it can't do complex logic, call services, or format data. That's by design. Sling Models fill that gap:
- →Separation of concerns — HTL handles rendering, Sling Models handle logic
- →Testability — Java classes are easy to unit test
- →Type safety — You get compile-time checking instead of runtime errors
- →Clean HTL — Your templates stay readable and maintainable
- →Service access — You can call OSGi services, APIs, and databases from a model
Without Sling Models, you'd either put logic in HTL (bad) or use scriptlets (very bad).
🏗️ What Happens in Real Projects?
Here's how Sling Models are used in a real project:
Scenario 1: Reading component properties
An author fills in a dialog with a heading, description, and link. Your Sling Model reads those JCR properties and exposes them as typed Java getters to HTL.
Scenario 2: Calling an external API
A "Latest News" component needs to fetch articles from a CMS API. The Sling Model injects a NewsService (OSGi service) and returns a list of NewsItem objects to HTL.
Scenario 3: Page-level data
A breadcrumb component needs the current page's ancestors. The Sling Model uses @ScriptVariable to get the currentPage and traverses the page tree.
Scenario 4: Exporter for headless
A Sling Model with @Exporter annotation exposes component data as JSON for headless delivery via Content Services.
⚙️ How It Works
Injection Strategy
When AEM instantiates your Sling Model, it goes through each field annotated with @Inject, @ValueMapValue, @ChildResource, etc., and tries to resolve the value from the adaptable (Resource or Request).
Adaptables
- →
Resource.class— Use when you only need JCR properties. Faster, simpler. - →
SlingHttpServletRequest.class— Use when you need request parameters, selectors, or suffixes. Can also access the resource through the request.
Key Annotations
| Annotation | What it does |
|---|---|
@Model | Marks the class as a Sling Model |
@ValueMapValue | Injects a JCR property by field name |
@Inject | Generic injection (tries multiple strategies) |
@OSGiService | Injects an OSGi service |
@ScriptVariable | Injects HTL script variables (currentPage, etc.) |
@ChildResource | Injects a child node as a Resource |
@RequestAttribute | Injects a request attribute |
@PostConstruct | Runs after all injections complete |
@Self | Injects the adaptable itself |
DefaultInjectionStrategy
Always use DefaultInjectionStrategy.OPTIONAL unless a field is truly required. With REQUIRED (the default), if any injection fails, the entire model fails to instantiate — causing a blank component.
💻 Example
A Complete Sling Model Example — A News Card component that reads JCR properties, calls an OSGi service, and exposes data to HTL.
🤔 Common Confusions
❓ When should I use Resource vs SlingHttpServletRequest as adaptable?
Use Resource when you only need JCR properties — it's simpler and faster. Use SlingHttpServletRequest when you need request context: selectors, suffixes, query parameters, or request attributes. If you're using @ScriptVariable or @RequestAttribute, you need the Request.
❓ Why does my Sling Model return null even though the property exists in CRXDE?
Most common causes: 1) The field name doesn't match the JCR property name — use @ValueMapValue(name="propertyName") to specify it explicitly. 2) You used DefaultInjectionStrategy.REQUIRED and something else failed, causing the whole model to fail silently. 3) The property is on a different node than you're adapting.
❓ What is the difference between @Inject and @ValueMapValue?
@Inject is generic — it tries multiple injection strategies in order. @ValueMapValue specifically reads from the ValueMap (JCR properties). Always prefer @ValueMapValue for JCR properties — it's explicit and faster.
❓ Can I use Sling Models for page-level logic, not just component logic?
Yes. You can adapt any Resource, including page nodes. Many projects create a "PageModel" that provides common page-level data (navigation, metadata, breadcrumbs) that all page templates use.
🚨 Common Production Issues
Model returns null
OSGi bundle not deployed, or bundle is in "Installed" state instead of "Active". Check Felix Console (/system/console/bundles).
PostConstruct throws NullPointerException
an injected OSGi service is null because the service isn't registered. Always null-check injected services.
Model works in Author but fails in Publish
an OSGi service that exists on Author isn't configured on Publish (e.g., a service that calls an internal Author API).
Sling Model not found
the resourceType in @Model doesn't match the component's sling:resourceType, or the package isn't in the bundle's scan path.
Circular injection
Model A injects Model B which injects Model A. AEM will throw a StackOverflowError.
Slow page rendering
a Sling Model calls an external API, QueryBuilder, or deep JCR traversal during every request. Move expensive work to a service with caching or precomputed content.
Intermittent data leakage
request-specific values are stored in static fields or singleton OSGi services instead of request-scoped model state.
Exporter JSON changes break a front-end app
a getter was renamed or removed without versioning the contract.
✅ Best Practices
Always use DefaultInjectionStrategy.OPTIONAL
it prevents the entire model from failing when one optional field is missing.
Use @ValueMapValue(name="propertyName") when the Java field name doesn't match the JCR property name.
Keep @PostConstruct lightweight
avoid heavy computations or API calls that could slow down every page render.
Write unit tests for every model using wcm.io AemContext
it saves hours of debugging.
Use the resourceType attribute in @Model to bind the model to a specific component, enabling automatic adaptation.
Prefer @OSGiService over manual service lookup
it's cleaner and testable.
Return empty collections (Collections.emptyList()) instead of null from list getters.
Keep external calls out of @PostConstruct unless they are fast, cached, timeout-protected, and truly required for rendering.
Treat Sling Model Exporter output as an API contract; add tests around JSON shape for headless or SPA consumers.
Use constructor or @PostConstruct validation to fail gracefully with author-friendly fallback output instead of blank components.
🏛️ Architect's Note
Sling Models are the single most important Java pattern in AEM development. Get this right and everything else becomes easier.
Architecture principle: Sling Models should be thin adapters, not service layers. If you find yourself writing complex business logic in a Sling Model, extract it into an OSGi service and inject it.
For AEM as a Cloud Service: Sling Models with @Exporter(name="jackson", extensions="json") automatically expose your component data as JSON endpoints — critical for headless and hybrid delivery patterns.
Versioning: When you change a Sling Model's interface significantly, consider creating a new version (e.g., HeroModelV2) rather than modifying the existing one — especially if other teams depend on it.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
↗ Where this topic fits
AEM Technology Guide
Return to the AEM learning hub.
AEM Developer Career Path
This AEM developer career path explains how to become an AEM developer through JCR, Sling, HTL, client libraries, Dispatcher, Cloud Service delivery, project depth, and interview-ready enterprise implementation skills.
Drupal Developer Career Path
A practical route through Drupal content modeling, site building, theming, modules, configuration management, APIs, performance, deployment discipline, and interview preparation.
Sources
Sources / References
Use these AEM references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide AEM learning path
Browse the full AEM topic collection and learning order on SplashRide.
AEM Interview Prep
Practice AEM interview questions and scenario-based answers.
AEM Developer Career Path
This AEM developer career path explains how to become an AEM developer through JCR, Sling, HTL, client libraries, Dispatcher, Cloud Service delivery, project depth, and interview-ready enterprise implementation skills.
Drupal Developer Career Path
A practical route through Drupal content modeling, site building, theming, modules, configuration management, APIs, performance, deployment discipline, and interview preparation.
AEM Components Explained: Dialogs, HTL, Sling Models, and Core Components
Learn AEM components with component nodes, dialogs, HTL, Sling Models, Core Components, authoring, inheritance, and real project patterns.
HTL (HTML Template Language)
Master HTL — AEM's templating language. Learn the syntax, best practices, and how it keeps your templates clean and secure.