N
Naveenr.dev
Chapter 07
19 min read2026-06-21

Understanding AEM Component Architecture

Learn how all AEM concepts work together. Understand component authoring, component rendering, component anatomy, and why architects think about components as rendering pipelines. Discover the complete lifecycle from author to browser.

Content Objective

In this chapter, you'll understand:

  • The complete journey of a component from authoring to rendering
  • Why authoring and rendering are completely separate processes
  • How resourceType connects content in the repository to code in /apps
  • Component anatomy and internal structure
  • Why dialogs only exist for authoring, not rendering
  • Common troubleshooting strategies used by experienced developers
  • Why architects think about components as rendering pipelines

Where We Are

In the previous chapters, you've learned the individual pieces:

  • Chapter 1: How requests flow through Dispatcher to AEM
  • Chapter 2: How ResourceResolver finds content
  • Chapter 3: How Components are resolved and scripts are located
  • Chapter 4: How request paths are decomposed (selectors, extensions, suffixes)
  • Chapter 5: How Sling Models adapt resources into Java objects
  • Chapter 6: How HTL generates the final HTML

You know how each piece works. But most people don't understand how they connect. When you ask an AEM developer "How does a component render?", many start describing individual pieces without connecting them.

This chapter is different. We're going to follow a real example all the way through and see exactly how everything connects.

Following a Real Example: Adding a Title

Let's follow exactly what happens when an author places a Title component on a page:

  1. The author drags a Title component onto the page
  2. They type: "Welcome To AEM"
  3. They click Save

A few seconds later the title appears on the page.

Looks simple. But what actually happened inside AEM?

What Really Happens When a Component Is Added

Almost any AEM website contains a collection of content blocks: header, navigation menu, hero banner, title, image, carousel, teaser, and footer.

To an end user, these appear to be part of a single web page.

However, AEM does not treat a page as one large piece of content.

Instead, AEM treats a page as a collection of smaller reusable building blocks called components.

Each component has a specific responsibility and can be reused across many pages.

This approach allows authors to build complex pages without requiring developers to create a new template for every business requirement.

The Authoring Phase

When an author saves a component to a page, AEM does not generate and store HTML.

Instead, AEM stores structured content inside the JCR repository.

Content Node Created
Content Node Created

A simplified example might look like this:

text
/content/site/en/home/jcr:content/root/title
│
├── sling:resourceType = project/components/title
└── title = Welcome To AEM

This is important to understand:

When authors create content, they're not creating web pages. They're storing data that can later be rendered in different ways.

Components vs Content: Know the Difference

Many new AEM developers mix these up.

Components = Code (lives in /apps)

text
/apps/project/components/title

Content = Data (lives in /content)

text
/content/site/en/home/jcr:content/root/title

The component knows how to render. The content is what gets rendered.

This separation is why AEM scales. One Title component can render thousands of different title values across thousands of pages.

The Role of resourceType

The content in the repository has two pieces of information:

  1. The actual value: title = Welcome To AEM
  2. The component reference: sling:resourceType = project/components/title

That sling:resourceType property is the glue. It tells Sling which component should eventually render this content.

The dialog collects the content but doesn't store anything else. Its job is done. The repository becomes the single source of truth.

At this point, nothing has been rendered yet.

Why resourceType Is Everything

In many ways, sling:resourceType plays a role similar to a class reference in object-oriented programming.

The content node contains the data, while the resourceType tells Sling which implementation should handle that data.

When a component is added to a page, AEM does not store references to HTL files, Sling Models, or Java classes. Instead, it stores a single property:

sling:resourceType = project/components/title

When Sling later encounters this content node, it uses the resourceType value to locate the corresponding component definition under /apps.

From there, Sling can find:

  • HTL scripts
  • Sling Models
  • Client-side assets
  • Inherited functionality through resourceSuperType

This is why resourceType is one of the most important properties in AEM. It acts as the bridge between content stored in the repository and code stored in the application.

Real Production Example: Missing Components After Migration

Consider a situation where content is migrated from one environment to another, but the corresponding component code is not deployed.

The content node still exists in the repository and all authored values are still available.

However, Sling cannot resolve:

sling:resourceType = project/components/title

because the component implementation does not exist under /apps.

The result is often:

  • Empty components
  • Missing content
  • Script resolution errors
  • Components rendering correctly in one environment but not another

This is one of the first things experienced AEM developers check when troubleshooting rendering issues.

Components Are More Than Just HTML

A component isn't just its HTML output. There are multiple layers working together:

  • Content — Author-entered data in the repository
  • Configuration — How the component behaves
  • Business Logic — Usually Sling Models
  • Presentation — HTML from HTL
  • Styling — CSS via Clientlibs
  • Behavior — JavaScript via Clientlibs

All of these together make a complete, reusable component.

Looking Inside a Real Component

Here's what a simple Title component actually contains:

text
/apps/project/components/title

├── .content.xml              (metadata)
├── cq:dialog                 (authoring interface)
├── title.html                (renders HTML)
├── _cq_editConfig.xml        (edit bars and config)
└── clientlibs/
    └── css/
        └── title.css
    └── js
        └── title.js         (styling)

Each piece does one thing:

  • cq:dialog — Collects input from authors
  • title.html — Generates the HTML
  • .content.xml — Component metadata
  • _cq_editConfig.xml — Edit configuration
  • clientlibs — CSS and JavaScript

The Sling Model (Java code) usually lives elsewhere:

text
core/src/main/java/com/project/core/models/TitleModel.java
Component Inside Example
Component Inside Example

The Dialog Is Just an Authoring Tool

This is one of the most misunderstood concepts. New developers think the dialog is somehow part of the component itself.

It's not.

The dialog's only job is to collect input from authors and store it in the repository.

When an author enters a title and clicks Save, the dialog doesn't generate any HTML. It doesn't participate in rendering. Its job ends the moment the content is stored.

The rendering happens later when Sling reads that stored content and renders it.

Simple flow:

text
Author → Dialog → Data Stored in Repository
(Rendering happens separately, much later)
Dialog vs Rendering in AEM
Dialog vs Rendering in AEM

Components Behave Differently on Author vs Publish

The component code is the same. The content is the same. The rendering pipeline is fundamentally the same, but Author includes additional capabilities designed for page editing and content creation.

But Author has extra features that Publish doesn't:

  • Edit bars
  • Component placeholders
  • Dialogs
  • Drag-and-drop editing
  • WCM Mode capabilities

These features help authors create. Publish strips all of this out and sends only the HTML to visitors.

This is why sometimes a component renders correctly on Author but looks different on Publish. It could be:

  • Permissions differences
  • Content not replicated
  • Client libraries not loading
  • Environment-specific configurations

What Actually Happens When a Visitor Requests a Page

So far we've been on the authoring side. Now let's see what happens when someone visits the page.

A visitor requests:

text
/content/site/en/home.html

Here's what Sling does:

  1. Locates the page resource
  2. Reads all child resources (title, image, etc.)
  3. For each resource, reads the sling:resourceType
  4. Finds the corresponding component in /apps
  5. If there's a Sling Model, adapts the resource to it
  6. Runs the HTL script to generate HTML
  7. Sends the final HTML to the browser

The browser never sees Sling Models, HTL files, or repository content. It only sees the final HTML.

How AEM Assembles a Complete Page

How AEM Build a Page
How AEM Build a Page

A real page has multiple components stacked together. During rendering, Sling processes each one:

text
/content/site/en/home/jcr:content

├── header
├── navigation
├── hero
├── title
├── image
├── teaser
└── footer

For each component, Sling:

  1. Finds the right implementation
  2. Executes the rendering logic
  3. Generates its HTML

The final page is the combined HTML output of all these individual components.

This is what makes AEM flexible yet organized. Authors can build complex pages, but developers keep code organized in reusable components.

How Component Resolution Happens Internally

To an end user, a page appears to be a single HTML document.

Internally, AEM treats each component as an independent resource.

Each child resource contains its own sling:resourceType value.

During rendering:

  1. Sling traverses the page hierarchy.
  2. Each resource is processed independently.
  3. Sling reads the resourceType.
  4. The appropriate component implementation is resolved.
  5. The component rendering lifecycle executes.
  6. HTML is generated for that component.

The final page delivered to the browser is simply the combined output of many independently rendered components.

This component-based rendering model is one of the reasons AEM supports highly flexible layouts while still enabling large-scale component reuse.

Component Inheritance: Reusing Existing Components

You don't always build components from scratch. Many components inherit from existing ones using sling:resourceSuperType.

A custom Title component might inherit from the Core Component Title, for example. Instead of copying everything, you create your own and only override what's different.

This saves work and keeps your codebase clean.

When Sling cannot find a script, dialog, or configuration in the current component, it continues traversing the sling:resourceSuperType chain until a matching implementation is found.

Real Example: Extending Core Component Title

Instead of copying the entire Core Title implementation, you create your own component that points to it:

xml
<jcr:content
  jcr:primaryType="cq:Component"
  sling:resourceSuperType="core/wcm/components/title/v3/title"
/>

Now your custom component inherits all Core Title functionality but can override specific parts.

Benefits:

  • Core updates automatically inherited
  • You only maintain your changes
  • Less technical debt
  • Better long-term sustainability

Why Clientlibs Matter

Rendering HTML isn't enough. Take a Carousel component.

The HTML might render fine, but without CSS the carousel looks broken. Without JavaScript it doesn't slide.

Clientlibs solve this by packaging CSS and JavaScript alongside the component. When AEM renders the page, it includes the necessary client-side assets so the component actually works in the browser.

This keeps everything organized and self-contained.

Clientlibs also help prevent duplicate CSS and JavaScript downloads by grouping assets into reusable categories that can be be included across multiple pages and components.

The Complete Journey: Title Component From Authoring to Browser

Component Journey
Component Journey

Let's trace our Title component all the way through:

text
Author enters: "Welcome To AEM"
          ↓
Dialog stores in repository
          ↓
Visitor requests: GET /content/site/en/home.html
          ↓
Sling locates resource
          ↓
Sling reads: sling:resourceType = project/components/title
          ↓
Component resolver finds: /apps/project/components/title
          ↓
Sling Model adapts resource to TitleModel
          ↓
Model retrieves title value from repository
          ↓
HTL script executes: <h1>Welcome To AEM</h1>
          ↓
Browser receives HTML

That simple component involved every concept you've learned.

Two Separate Worlds: Authoring vs Rendering

Think of these as two completely separate processes happening at different times:

Authoring (When authors work):

text
Author → Dialog → Save Content

Rendering (When visitors visit):

text
Request → Resource → resourceType → Sling Model → HTL → HTML

These lifecycles are related but totally separate. Understanding this makes AEM much easier to think about.

Troubleshooting: Debugging Components

When a component doesn't render correctly, experienced developers don't start by opening the HTL file.

Instead, they validate each stage of the rendering pipeline in order:

  1. Resource exists? — Check error.log, browse repository
  2. resourceType present? — Verify the property is set
  3. Component path exists? — Check /apps/
  4. Model adapting? — Test model instantiation
  5. HTL executing? — Look for script errors
  6. Clientlibs loading? — Check page source

By following the same order Sling uses, you find problems faster.

CheckWhat to VerifyCommon Issue
Resource existsIs content in repository?Not replicated to Publish
resourceType setDoes resource have the property?Typo or missing
Component pathDoes /apps/ exist?Not deployed
HTL scriptDoes title.html exist?Missing file
Script resolutionRight selector/extension?Wrong request path
Model adaptsCan resource adapt to Model?Wrong annotations
Injections workAre @ValueMapValue fields populated?Property names don't match
Clientlibs loadAre CSS/JS in page source?Categories missing
PermissionsCan user read content?Access denied
LogsAny errors in error.log?Java exceptions

Why Experienced Developers Troubleshoot in This Order

Experienced AEM developers follow this sequence because every stage depends on the stage before it.

Think about the rendering pipeline:

text
Resource Exists?
      ↓
resourceType Present?
      ↓
Component Exists?
      ↓
Model Adapting?
      ↓
HTL Executing?
      ↓
Clientlibs Loading?

For example:

  • If the resource does not exist, HTL is irrelevant.
  • If the resourceType is incorrect, Sling cannot locate the component.
  • If the component cannot be resolved, the model will never execute.
  • If the model fails to adapt, HTL may not receive the required data.
  • If the HTML renders correctly but styling is missing, the issue may be in Clientlibs.

By validating the pipeline in the same order Sling executes it, root causes can usually be identified much faster.

How Architects Think About Components

Architects don't see components as folders or files.

They see them as rendering pipelines.

Here's the mental model:

text
Content (Repository)
    ↓
resourceType (Pointer)
    ↓
Component Implementation (/apps)
    ↓
Sling Model (Business Logic)
    ↓
HTL (Presentation)
    ↓
Clientlibs (Styling & Behavior)
    ↓
HTML (Output)

Key insight: The repository doesn't store references to HTL files or Java classes. It only stores a sling:resourceType value.

That single property is all that connects content to implementation. This design keeps content independent from code, which is why AEM can scale.

Experienced architects rarely start with HTL or Java code. Instead, they follow the same rendering chain Sling uses internally. By validating each stage in order, root causes become much easier to identify.

What You Should Take Away

  • Components are building blocks, not monolithic pages
  • Content and code are separate — components implement, content stores data
  • sling:resourceType connects content to implementation
  • Authoring and rendering are separate lifecycles
  • Dialog only exists for authoring; it doesn't participate in rendering
  • Author and Publish environments are different
  • Inheritance through resourceSuperType reduces duplication
  • Clientlibs package CSS/JavaScript with components
  • Troubleshoot by tracing the rendering pipeline
  • Architects think in rendering pipelines, not code folders

What Happens Next?

In this chapter, we tied together everything you've learned about request flow, resources, components, Sling Models, and HTL.

The foundation is solid. Now you're ready for the more advanced topics:

  • Chapter 8: Understanding Templates — How page blueprints are defined
  • Chapter 9: Layout Container & Responsive Grid — How pages become responsive
  • Chapter 10: Style System — How design consistency is enforced without duplicate components
  • Chapter 11: Template Policies Deep Dive — How governance scales across multi-site implementations

You now understand not just individual AEM concepts, but how they work together at scale.

That's the real skill.

Understanding individual concepts is important. Understanding how content, resource resolution, component resolution, Sling Models, HTL, and Clientlibs work together is what separates an AEM developer from an AEM architect.

Want to See This Applied to a Real Problem?

Read Real-World Scenario — When and Why You Actually Reach for an AEM Component for honest criteria on when a component earns its complexity versus when it's overkill, plus the dialog-bloat and resourceSuperType gotchas that show up once components are actually in production.

Enjoyed this chapter?

Get an email when I publish the next chapter. No spam — just new technical deep-dives.

Comments

Share feedback or questions about this blog post.

No comments yet. Be the first to share your thoughts.