AEM Dialog Components #5: PathBrowser - AEM Content Selection
AEM Dialog Components #5: PathBrowser
What is a PathBrowser?
The PathBrowser (or PathField) is a Granite UI component that allows authors to select content from the AEM repository through a navigation interface.
Common use cases:
- Internal page selection
- Content linking
- Image selection from DAM
- Asset references
- Experience Fragment selection
Basic Configuration
Minimal XML Dialog
<pagePath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Page"
name="./pagePath"
rootPath="/content"/>Essential properties:
sling:resourceType- Alwaysgranite/ui/components/coral/foundation/form/pathfieldfieldLabel- Labelname- JCR path (always use./)rootPath- Navigation root path
Main Properties
1. rootPath (Root Path)
<!-- Pages under /content -->
<pagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Internal Page"
name="./pagePath"
rootPath="/content/mysite"/>
<!-- Assets from DAM -->
<imagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
name="./imagePath"
rootPath="/content/dam"/>Best practice: Always limit to a specific subpath!
2. filter (Resource Type Filter)
<!-- Pages only -->
<pagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Page Link"
name="./pagePath"
rootPath="/content/mysite"
filter="hierarchyNotFile"/>
<!-- Images only -->
<imagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
name="./imagePath"
rootPath="/content/dam"
filter="image"/>Common filters:
hierarchyNotFile- Pages only (no assets)image- Images onlyvideo- Videos onlyfile- Files only- Custom predicates
3. Required
<imagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Hero Image"
name="./imagePath"
rootPath="/content/dam/mysite"
filter="image"
required="{Boolean}true"/>4. forceSelection (Force Browser Selection)
<pagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Page"
name="./pagePath"
rootPath="/content"
forceSelection="{Boolean}true"/>Behavior: Disables manual input, forces browser usage.
Practical Examples
Example 1: Internal Page Link
<internalLink
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Internal Link"
fieldDescription="Select a page from your site"
name="./internalLink"
rootPath="/content/mysite"
filter="hierarchyNotFile"/>HTL:
<a href="${properties.internalLink}.html">
${properties.linkText}
</a>Example 2: Image from DAM
<fileReference
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
fieldDescription="Select an image from DAM"
name="./fileReference"
rootPath="/content/dam/mysite"
filter="image"
required="{Boolean}true"/>HTL:
<img src="${properties.fileReference}" alt="${properties.altText}"/>Example 3: Experience Fragment
<experienceFragment
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Experience Fragment"
fieldDescription="Select a header experience fragment"
name="./experienceFragment"
rootPath="/content/experience-fragments/mysite"
filter="folder,cq:Page"/>Example 4: Video from DAM
<videoPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Video"
name="./videoPath"
rootPath="/content/dam/mysite/videos"
filter="video"/>HTL:
<video controls>
<source src="${properties.videoPath}" type="video/mp4"/>
</video>Example 5: PDF or Document
<documentPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Download Document"
fieldDescription="Select a PDF or document"
name="./documentPath"
rootPath="/content/dam/mysite/documents"
filter="file"/>HTL:
<a href="${properties.documentPath}" download>
Download ${properties.documentTitle}
</a>Complete Dialog with PathBrowser
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:granite="http://www.adobe.com/jcr/granite/1.0"
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"
jcr:primaryType="nt:unstructured"
jcr:title="Content Card"
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">
<items jcr:primaryType="nt:unstructured">
<!-- Content Tab -->
<content
jcr:primaryType="nt:unstructured"
jcr:title="Content"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<!-- Title -->
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Title"
name="./title"
required="{Boolean}true"/>
<!-- Image from DAM -->
<fileReference
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
fieldDescription="Select card image"
name="./fileReference"
rootPath="/content/dam/mysite"
filter="image"
required="{Boolean}true"/>
<!-- Alt Text -->
<altText
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Alt Text"
name="./altText"
required="{Boolean}true"/>
<!-- Link to Page -->
<linkPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Link"
fieldDescription="Link to internal page"
name="./linkPath"
rootPath="/content/mysite"
filter="hierarchyNotFile"/>
</items>
</content>
</items>
</tabs>
</items>
</content>
</jcr:root>Using in HTL
Internal Page
<a data-sly-test="${properties.linkPath}"
href="${properties.linkPath}.html">
${properties.linkText}
</a>Image from DAM
<img data-sly-test="${properties.fileReference}"
src="${properties.fileReference}"
alt="${properties.altText || properties.title}"/>With Sling Model
@Model(adaptables = Resource.class)
public class ContentCardModel {
@ValueMapValue
private String title;
@ValueMapValue
private String fileReference;
@ValueMapValue
private String altText;
@ValueMapValue
private String linkPath;
public String getTitle() {
return title;
}
public String getImagePath() {
return fileReference;
}
public String getAltText() {
return altText != null ? altText : title;
}
public String getLinkUrl() {
return linkPath != null ? linkPath + ".html" : null;
}
public boolean hasImage() {
return fileReference != null && !fileReference.isEmpty();
}
public boolean hasLink() {
return linkPath != null && !linkPath.isEmpty();
}
}HTL with Model:
<div data-sly-use.model="com.mysite.models.ContentCardModel" class="card">
<!-- Image -->
<img data-sly-test="${model.hasImage}"
src="${model.imagePath}"
alt="${model.altText}"
class="card-image"/>
<!-- Title -->
<h3 class="card-title">${model.title}</h3>
<!-- Link -->
<a data-sly-test="${model.hasLink}"
href="${model.linkUrl}"
class="card-link">
Read More
</a>
</div>Advanced Filters
Custom Filter with Predicate
<customPathField
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Custom Filter"
name="./customPath"
rootPath="/content"
filter="myCustomFilter"
optionLoader="com.mysite.predicates.CustomPathFieldPredicateLoader"/>Java Predicate Loader
@Component(service = PredicateLoader.class)
public class CustomPathFieldPredicateLoader implements PredicateLoader {
@Override
public Predicate load(Request request) {
return new Predicate() {
@Override
public boolean includes(Resource resource) {
// Custom logic to filter resources
return resource.getPath().contains("specific-folder");
}
};
}
}PathField vs PathBrowser
They are synonyms - same component, different names:
granite/ui/components/coral/foundation/form/pathfieldgranite/ui/components/coral/foundation/form/pathbrowser
Use pathfield (more common in documentation).
Common Issues
❌ Issue 1: rootPath too broad
<!-- NOT RECOMMENDED - allows navigation anywhere -->
<pagePath
rootPath="/"/>
<!-- BETTER - limits to your area -->
<pagePath
rootPath="/content/mysite"/>Why: Too much freedom confuses authors and allows wrong selections.
❌ Issue 2: Missing .html in HTL
<!-- WRONG - path without extension -->
<a href="${properties.linkPath}">Link</a>
<!-- CORRECT - add .html -->
<a href="${properties.linkPath}.html">Link</a>
<!-- OR use Link Externalizer in Model -->❌ Issue 3: Image path without validation
<!-- RISKY - if path is empty -->
<img src="${properties.fileReference}"/>
<!-- SAFE - check existence -->
<img data-sly-test="${properties.fileReference}"
src="${properties.fileReference}"
alt="${properties.altText}"/>❌ Issue 4: Incorrect filter
<!-- WRONG - filter on images but root is not DAM -->
<image
rootPath="/content/mysite"
filter="image"/>
<!-- CORRECT - root on DAM for images -->
<image
rootPath="/content/dam"
filter="image"/>Best Practices
- ✅ Specific rootPath: Always limit to a logical subpath
- ✅ Appropriate filter: Use filter to guide selection
- ✅ Required for critical assets: Hero image, logo, etc.
- ✅ fieldDescription: Explain what to select
- ✅ HTL validation: Always check that the path exists
- ✅ .html for pages: Add extension in links
- ✅ Mandatory alt text: For images, always require alt text
- ✅ Link Externalizer: Use in Model for external URLs
PathBrowser vs FileUpload
| PathBrowser | FileUpload | |
|---|---|---|
| Use | Select existing content | Upload new file |
| Source | DAM / Content | Author's local |
| When | Images already in DAM | Upload new images |
Rule: If the content is already in AEM, use PathBrowser!
Advanced Patterns
1. External or Internal Link
<!-- Checkbox: External link? -->
<isExternalLink
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./isExternalLink"
text="External link"
value="{Boolean}true"
uncheckedValue="{Boolean}false"/>
<!-- PathBrowser: Internal link (if not external) -->
<internalLink
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Internal Link"
name="./internalLink"
rootPath="/content/mysite"
filter="hierarchyNotFile"
granite:hide="${properties.isExternalLink}"/>
<!-- Textfield: External URL (if external) -->
<externalUrl
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="External URL"
name="./externalUrl"
granite:hide="${!properties.isExternalLink}"/>Sling Model:
public String getLinkUrl() {
if (isExternalLink && externalUrl != null) {
return externalUrl;
} else if (internalLink != null) {
return internalLink + ".html";
}
return null;
}2. Image with Renditions
<picture>
<source srcset="${properties.fileReference}./_jcr_content/renditions/cq5dam.web.1280.1280.jpeg"
media="(min-width: 1024px)"/>
<source srcset="${properties.fileReference}./_jcr_content/renditions/cq5dam.web.640.640.jpeg"
media="(min-width: 640px)"/>
<img src="${properties.fileReference}"
alt="${properties.altText}"/>
</picture>Next Lesson
In the next lesson we'll explore the RichText Editor component for formatted content.
Resources:
Guide #5 of the AEM Dialog Components series - ← Previous lesson | Next lesson →