> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Your First Component

## Component Structure and Building Your First Component in AEM

Components are the primary building blocks of AEM pages. A button, a text block, a carousel, or a hero banner — each of these elements is a component. In this article, we will build a component from scratch and understand every file involved.

***

## 1. The Component Triad

Every complete AEM Component is typically made up of three main parts (the triad). Not every component requires all three (an extremely simple component might only need HTL), but most real-world components use this architecture:

| Component        | File Type            | Purpose                                                                                          |
| ---------------- | -------------------- | ------------------------------------------------------------------------------------------------ |
| **Dialog**       | XML (`.content.xml`) | Defines the Authoring UI — input form fields for content authors.                                |
| **HTL Template** | `.html`              | Responsible for rendering HTML markup based on data provided by the Model.                       |
| **Sling Model**  | `.java`              | Reads content data from JCR, processes business logic, and safely supplies data to the Template. |

```mermaid
graph TD
    A["AEM Component"] --> B["Dialog<br/>_cq_dialog/.content.xml"]
    A --> C["HTL Template<br/>hello.html"]
    A --> D["Sling Model<br/>HelloModel.java"]

    B -->|"Author inputs & saves data"| JCR[("JCR Node")]
    D -->|"Reads properties"| JCR
    C -->|"Uses & calls Getters"| D
    C -->|"Renders to"| Browser(("Browser"))
```

***

## 2. Project File Structure

A Component's source code is split across two separate locations (modules): UI (Frontend/Config) and Logic (Backend).

**UI Module (Inside `ui.apps`):**

```text [ui.apps structure]
ui.apps/src/main/content/jcr_root/apps/mysite/components/
└── hello/
    ├── .content.xml        # Component definition (cq:Component definition)
    ├── hello.html          # HTL template (Display markup)
    └── _cq_dialog/
        └── .content.xml    # Author dialog (Authoring UI)
```

**Backend Module (Inside `core`):**

```text [core structure]
core/src/main/java/com/mysite/core/models/
└── HelloModel.java         # Sling Model
```

***

## 3. Steps to Build a Component

### Step 1: Component Definition

The `.content.xml` file in the component root folder defines the `cq:Component` node:

```xml [.content.xml]
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="Hello"
    jcr:description="A simple greeting component"
    componentGroup="My Site - Content"/>
```

| Property          | Function                                                                               |
| ----------------- | -------------------------------------------------------------------------------------- |
| `jcr:primaryType` | Must be `cq:Component`                                                                 |
| `jcr:title`       | Display name in the Component Browser list                                             |
| `jcr:description` | Tooltip description on mouse hover                                                     |
| `componentGroup`  | Component grouping. Use a common group (e.g. `"My Site - Content"`) for easier search. |

### Step 2: Write HTL Template

HTL (HTML Template Language) handles rendering HTML. Create `hello.html`:

```html [hello.html]
<div class="cmp-hello" data-sly-use.model="com.mysite.core.models.HelloModel">
    <h2 class="cmp-hello__title">${model.greeting}</h2>
    <p class="cmp-hello__message">${model.message}</p>
</div>
```

**This template performs 3 actions:**

1. Uses `data-sly-use` to instantiate the Sling Model (Java class).
2. Retrieves data from the Model using `$\{model.property\}` syntax.
3. Outputs HTML adhering to **BEM** naming conventions (`cmp-` prefix) — standard for AEM Core Components.

### Step 3: Create Sling Model

The Sling Model reads values from the JCR node and prepares data for HTL:

```java [HelloModel.java]
package com.mysite.core.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class HelloModel {

    @ValueMapValue
    @Default(values = "Hello, World!")
    private String greeting;

    @ValueMapValue
    @Default(values = "This is my first AEM component.")
    private String message;

    public String getGreeting() { return greeting; }
    public String getMessage() { return message; }
}
```

**Annotation Explanations:**

| Annotation                            | Meaning                                                                    |
| ------------------------------------- | -------------------------------------------------------------------------- |
| `@Model(adaptables = Resource.class)` | Declares this class as a Sling Model adapted from a `Resource` (JCR Node). |
| `@ValueMapValue`                      | Injects values from the component's JCR properties into this variable.     |
| `@Default`                            | Provides a default value if the Author hasn't entered anything yet.        |

> **Important:** If you omit `defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL`, Sling Model's default strategy is `REQUIRED`. If an author drags the component onto a page without entering form data (no JCR data yet), the model fails and throws an error instead of displaying defaults. **Always use `OPTIONAL`.**

### Step 4: Create Author Dialog

The Dialog defines which input fields an author sees. Create `_cq_dialog/.content.xml`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Hello"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/tabs" maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <!-- Properties Tab -->
                    <properties jcr:primaryType="nt:unstructured" jcr:title="Properties" sling:resourceType="granite/ui/components/coral/foundation/container" margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns" margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <!-- Greeting Input Field -->
                                            <greeting jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                fieldLabel="Greeting"
                                                name="./greeting"
                                                required="{Boolean}true"/>
                                            <!-- Message Input Field -->
                                            <message jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Message"
                                                name="./message"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>
```

> **Note:** The `name` attribute (such as `name="./greeting"`) is critical. It defines the property name saved in JCR and must match the variable injected in your Sling Model (`private String greeting;`).

***

## 4. Component Rendering Flow

What actually happens when a user loads a page containing your component?

```mermaid
sequenceDiagram
    participant Browser as Browser
    participant Sling as Sling Request Processor
    participant JCR as JCR Data
    participant Model as Sling Model
    participant HTL as HTL Engine

    Browser->>Sling: HTTP GET /content/mysite/en/home.html
    Sling->>JCR: Finds Page & Component Nodes
    Sling->>HTL: Calls hello.html (to render Component node)
    HTL->>Model: data-sly-use (Instantiates HelloModel)
    Model->>JCR: @ValueMapValue (Reads ./greeting, ./message)
    JCR-->>Model: Returns values
    Model-->>HTL: Model Ready
    HTL->>HTL: Compiles ${model.greeting} to HTML
    HTL-->>Sling: Complete HTML string
    Sling-->>Browser: Returns HTTP 200 (HTML displayed)
```

***

## 5. Deployment and Testing

1. **Build source code:** In project root (`mysite`), run:

   ```bash
   mvn clean install -PautoInstallSinglePackage
   ```

2. **Drag & drop component onto a page:**
   - Open AEM: `http://localhost:4502/sites.html`
   - Edit a page (Edit mode).
   - Open Side Panel, search for `Hello` and drag it onto the page.
   - Click component (wrench icon) to open Dialog, enter content and save.

3. **Verify in CRXDE Lite:** Browse to the page path, navigate to the component node you dropped, and verify stored data:

   ```text
   hello
   ├── jcr:primaryType = "nt:unstructured"
   ├── sling:resourceType = "mysite/components/hello"
   ├── greeting = "Welcome to My Site"        (<- From Dialog)
   └── message = "We build amazing things."   (<- From Dialog)
   ```

***

## 6. Debug Playbook: From Dialog to HTML (4 Checkpoints)

If your component does not render content as expected, use this 4-step check to isolate the issue:

```mermaid
flowchart TD
    Start(["Component text missing/broken?"]) --> S1["1. Dialog Save Check"]
    S1 --> Q1{"Reopen Dialog,<br/>are values preserved?"}

    Q1 -->|No| Fix1["Dialog UI Error: Invalid XML structure<br/>or missing 'name' attribute"]
    Q1 -->|Yes| S2["2. JCR Check"]

    S2 --> Q2{"CRXDE Lite shows property<br/>saved on component Node?"}

    Q2 -->|No| Fix2["Storage Error: Check write permissions<br/>or parent node structure"]
    Q2 -->|Yes| S3["3. Sling Model Check"]

    S3 --> Q3{"Does Model inject<br/>data correctly?"}

    Q3 -->|No| Fix3["Model Error: Invalid @ValueMapValue,<br/>getter issue, missing DefaultInjectionStrategy"]
    Q3 -->|Yes| S4["4. HTL Check"]

    S4 --> Q4{"Is HTL output empty?"}

    Q4 -->|Yes| Fix4["HTL Error: Invalid data-sly-use path,<br/>wrong model variable name model.abc"]
    Q4 -->|No| End(["Everything works properly ✅"])
```

***

## 7. Proxy Components (AEM Core Components)

In real project scenarios, instead of building basic components like "Text" or "Image" from scratch, we use **Core Components** (provided out-of-the-box by Adobe) via the **Proxy Component** design pattern.

Create a lightweight component pointing to a Core Component via `sling:resourceSuperType`:

```xml
<!-- apps/mysite/components/text/.content.xml -->
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="Text"
    componentGroup="My Site - Content"
    sling:resourceSuperType="core/wcm/components/text/v2/text"/>
```

Thanks to `sling:resourceSuperType`, your component inherits all features, dialogs, and Sling Models from `core/wcm/components/text/v2/text`. You only override specific files (such as HTL) if you want custom markup. The Maven archetype generates these proxies automatically when initializing a project.
