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

# JCR Page Properties & jcr:content Node

## Learning Objectives

By the end of this page, you will be able to:

- Understand the two-node structure of an AEM page (`cq:Page` + `jcr:content`)
- Identify and use the most common JCR properties for page rendering, SEO, and replication
- Distinguish between similar properties (e.g., `jcr:title` vs `pageTitle` vs `navigationTitle`)
- Read page properties programmatically using Java and Groovy

***

## What is jcr:content?

Think of an AEM page as a **folder with a single file inside it**. The folder is the container; the file holds all the actual data. In JCR terms:

```
/content/mysite/en/home          ← cq:Page node (container)
└── jcr:content                  ← cq:PageContent node (actual data)
    ├── jcr:primaryType
    ├── jcr:title
    ├── cq:template
    ├── sling:resourceType
    ├── ... (all properties)
    └── par/                     ← child nodes (components)
```

`cq:Page` is just a wrapper. All data lives in `jcr:content`.

This two-node pattern is fundamental to how AEM works. Every page you create in the Sites console follows this structure. Understanding it is the key to reading, writing, and debugging page content.

***

## Core JCR Properties

These are the properties that AEM and Sling use most frequently to render pages.

### `jcr:primaryType`

Defines the node type. The value determines what the node is and what it can contain.

| Value                 | Meaning                                         |
| --------------------- | ----------------------------------------------- |
| `cq:Page`             | Page container node                             |
| `cq:PageContent`      | `jcr:content` node holding data                 |
| `nt:unstructured`     | Free-form node, no schema (used for components) |
| `sling:Folder`        | Folder that can contain resources               |
| `sling:OrderedFolder` | Folder that preserves child node order          |
| `oak:Unstructured`    | Same as `nt:unstructured`, Oak-specific variant |

```properties
jcr:primaryType = "cq:PageContent"
```

***

### `jcr:title`

The title displayed in the AEM UI (Sites console, breadcrumb). This is **not** the HTML `<title>` tag.

```properties
jcr:title = "Home Page"
```

:::tip\[pageTitle vs navigationTitle vs jcr:title]

- `jcr:title` → Sites console UI display
- `pageTitle` → HTML `<title>` tag (if the component uses it)
- `navigationTitle` → Menu/nav label

If you only set `jcr:title`, most components use it as a fallback for all three.
:::

***

### `jcr:description`

The meta description for the page. Used by the SEO head component.

```properties
jcr:description = "Homepage of mysite"
```

***

### `sling:resourceType`

Points to the component that renders `jcr:content`. **The most important property** for Sling rendering.

```properties
sling:resourceType = "mysite/components/page"
```

Sling resolves this by looking up `/apps/mysite/components/page/` and finding `page.html` or the appropriate script.

:::warning\[AMS Override]
On AMS (Adobe Managed Services), `/libs` is not editable. Your custom `sling:resourceType` must point to `/apps`, not `/libs`.
:::

***

### `cq:template`

The template used to create the page. Affects editable regions when using Editable Templates.

```properties
cq:template = "/conf/mysite/settings/wcm/templates/content-page"
```

| Template type     | Path                                      |
| ----------------- | ----------------------------------------- |
| Static template   | `/apps/mysite/templates/content-page`     |
| Editable template | `/conf/mysite/settings/wcm/templates/...` |

:::note
`cq:template` is only a reference. If the template is deleted, the page still renders normally because `sling:resourceType` is more important for rendering.
:::

***

### `cq:lastModified` / `cq:lastModifiedBy`

```properties
cq:lastModified = "{Date}2024-01-15T10:30:00.000+07:00"
cq:lastModifiedBy = "author@mysite.com"
```

Set automatically when an author saves a page. Used for:

- Displaying "Last modified" in the UI
- Dispatcher cache invalidation (if `statfileslevel` is configured)
- Audit trail

***

### `jcr:lastModified` / `jcr:lastModifiedBy`

JCR-level modification, different from CQ-level. Usually equal, but can differ if a node is modified directly via API without going through a CQ workflow.

```properties
jcr:lastModified = "{Date}2024-01-15T10:30:00.000+07:00"
jcr:lastModifiedBy = "admin"
```

***

### `jcr:created` / `jcr:createdBy`

```properties
jcr:created = "{Date}2024-01-10T08:00:00.000+07:00"
jcr:createdBy = "admin"
```

Read-only after the node is created. Set once and never changes.

***

## Page Metadata Properties

These properties control how the page appears in navigation, search results, and social sharing.

### `pageTitle`

Overrides `jcr:title` for the HTML `<title>` tag. If not set, most components fall back to `jcr:title`.

```properties
pageTitle = "Home | MyBrand"
```

### `navigationTitle`

Overrides the label shown in navigation components.

```properties
navigationTitle = "Home"
```

### `hideInNav`

Boolean. Hides the page from navigation components (e.g., the Nav component in Core Components).

```properties
hideInNav = {Boolean}true
```

***

## SEO & Social Properties

### `cq:tags`

Array of tag references pointing to `/content/cq:tags/`.

```properties
cq:tags = [
  "mysite:category/news",
  "mysite:region/vietnam"
]
```

Used with the `TagManager` API to filter pages and display them in Search.

```java
// Java: get tags from a page
Tag[] tags = page.getTags();
```

### `sling:vanityPath`

Vanity URL redirect. `/content/mysite/en/home` → `/home`.

```properties
sling:vanityPath = "/home"
```

:::danger\[Performance Warning]
Sling reads ALL vanity paths at startup and caches them in memory. Too many vanity paths (\~10k+) cause slow startup and memory pressure. On AMS, consider using Dispatcher rewrite rules instead.
:::

### `redirectTarget`

Redirects the page to a different URL. Used with `RedirectServlet`.

```properties
redirectTarget = "/content/mysite/en/new-home"
```

***

## Replication Properties

Set by the Replication Agent after publishing or unpublishing. Used to check whether a page has been published and to audit replication history.

### `cq:lastReplicated` / `cq:lastReplicatedBy` / `cq:lastReplicationAction`

```properties
cq:lastReplicated = "{Date}2024-01-15T11:00:00.000+07:00"
cq:lastReplicatedBy = "admin"
cq:lastReplicationAction = "Activate"   # or "Deactivate"
```

```java
// Check if a page is published
ValueMap vm = resource.getValueMap();
Calendar replicated = vm.get("cq:lastReplicated", Calendar.class);
String action = vm.get("cq:lastReplicationAction", String.class);
boolean isPublished = "Activate".equals(action) && replicated != null;
```

***

## Design & Styling Properties

### `cq:designPath`

Design path override for static templates (legacy).

```properties
cq:designPath = "/etc/designs/mysite"
```

:::note
Editable Templates do not use `cq:designPath`. This property is only needed for Static Templates.
:::

***

## Versioning Properties

### `jcr:isCheckedOut`

Workspace versioning. If `true`, the node is locked by a user.

### `jcr:baseVersion`

Reference to the current version in the version history.

***

## Workflow Properties

### `cq:wfStatus` / `cq:wfId`

Set by AEM Workflow when a page is in a workflow process.

```properties
cq:wfStatus = "RUNNING"
cq:wfId = "..."
```

***

## Custom / App-Specific Properties

Custom properties are defined in component dialogs. Convention:

```properties
# Avoid reserved namespaces (jcr:, sling:, cq:, nt:)
# Use plain names or a custom namespace

heroImage = "/content/dam/mysite/hero.jpg"
ctaLabel   = "Learn More"
ctaLink    = "/content/mysite/en/about"
```

***

## Common Node Types in jcr:content

```
jcr:content                    (cq:PageContent)
├── par                        (nt:unstructured) ← parsys
│   ├── hero                   (nt:unstructured) ← component instance
│   │   ├── sling:resourceType = "mysite/components/hero"
│   │   └── heroImage          = "/content/dam/..."
│   └── text_123456            (nt:unstructured)
│       ├── sling:resourceType = "core/wcm/components/text/v2/text"
│       └── text               = "<p>Hello</p>"
├── root                       (nt:unstructured) ← editable template root
│   └── responsivegrid         (nt:unstructured)
└── image                      (nt:unstructured) ← featured image node
    └── fileReference          = "/content/dam/mysite/image.jpg"
```

***

## Quick Reference: Namespace Meanings

| Namespace | Owner           | Meaning                                  |
| --------- | --------------- | ---------------------------------------- |
| `jcr:`    | JCR spec        | Core repository metadata                 |
| `nt:`     | JCR spec        | Node types                               |
| `sling:`  | Sling framework | Routing, scripting, resource resolution  |
| `cq:`     | Adobe/AEM       | AEM-specific page/component metadata     |
| `dam:`    | Adobe/AEM       | DAM asset metadata                       |
| `oak:`    | Apache Oak      | Oak-specific storage hints               |
| `rep:`    | Jackrabbit      | Access control, security                 |
| `mix:`    | JCR spec        | Mixin types (versionable, referenceable) |

***

## Reading Properties in Java

```java title="ReadPageProperties.java"
@Model(adaptables = SlingHttpServletRequest.class)
public class PageModel {

    @ScriptVariable
    private Page currentPage;

    public String getTitle() {
        // jcr:title fallback chain
        ValueMap props = currentPage.getProperties();
        return props.get("pageTitle", props.get("jcr:title", ""));
    }

    public boolean isPublished() {
        ValueMap props = currentPage.getProperties();
        return "Activate".equals(props.get("cq:lastReplicationAction", String.class));
    }

    public String[] getTags() {
        Tag[] tags = currentPage.getTags();
        return Arrays.stream(tags)
                     .map(Tag::getTitle)
                     .toArray(String[]::new);
    }
}
```

***

## CRXDE Quick Check

Browse page properties in CRXDE:

```
http://localhost:4502/crx/de/index.jsp#/content/mysite/en/home/jcr:content
```

Or use the Groovy Console to dump all properties:

```groovy
def page = pageManager.getPage('/content/mysite/en/home')
def props = page.properties
props.each { k, v -> println "$k = $v" }
```

***

## Reflection

- If you delete a template from the repository, will the pages created with it still render? Why or why not?
- What is the difference between `cq:lastModified` and `jcr:lastModified`? When would they diverge?
- How would you check whether a page has been published using the properties discussed here?

***

## What's Next

Now that you understand JCR page properties, explore:

- [Sling Models](/docs/aem/foundation/sling-models) - How to read these properties in Java using the adaptable pattern
- [Core Components](/docs/aem/foundation/core-components) - How Core Components use these properties to render pages
- [AEM Page Manager API](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/developing/api-reference/com/adobe/granite/core/api/reference/package-summary) - The full Java API for working with pages
