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.

A simplified example might look like this:

/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)

/apps/project/components/title

Content = Data (lives in /content)

/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.

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:

/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         (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:

core/src/main/java/com/project/core/models/TitleModel.java

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:

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

Components Behave Differently on Author vs Publish

The component code is the same. The content is the same. The rendering process is mostly the same.

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:

/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

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

/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.

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.

Real Example: Extending Core Component Title

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

<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.

The Complete Journey: Title Component From Authoring to Browser

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

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):

Author → Dialog → Save Content

Rendering (When visitors visit):

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

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:

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.

The Troubleshooting Mindset

Experienced architects trace the rendering chain instead of jumping into code:

  1. Does the resource exist?
  2. Is resourceType set correctly?
  3. Can Sling find the component implementation?
  4. Can the model adapt correctly?
  5. Is HTL executing without errors?
  6. Are clientlibs loading?

This approach finds problems faster because you're tracing exactly what Sling does internally.

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.

Enjoyed this chapter?

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