aem-dialog-generator-plugin
v2.0.6
Published
Webpack plugin that automatically generates AEM component dialog XML, design dialogs, and policies from JSON configurations
Downloads
78
Maintainers
Readme
AEM Dialog Generator Plugin
A Webpack plugin that automatically generates AEM component _cq_dialog.xml and _cq_design_dialog.xml files with associated policies from simple JSON configurations. Features intelligent automation including automatic style tab generation, policy-to-template mapping, and dynamic indentation for production-ready AEM components.
Features
✨ 27 Field Types Supported - textfield, textarea, select, pathfield, pagefield, checkbox, multifield, RTE, fieldset, container, heading, text/alert, tags, image, autocomplete, radiogroup, contentfragmentpicker, experiencefragmentpicker, assetpicker, hidden, button, well, and more
🎨 Flexible Layouts - Tabs, simple layouts, accordion, or fieldsets for organization
🔄 Auto-generation - XML files generated on every webpack build
📝 Simple JSON - Easy-to-read configuration instead of verbose XML
🎯 Multifield Support - Both simple and composite multifields
📋 Rich Text Editor - Full RTE configuration with customizable features
🗂️ Folder Structure - Supports both _cq_dialog/.content.xml and _cq_dialog.xml formats
🎭 Show/Hide - Dynamic field visibility based on dropdown or checkbox values
📷 Image Upload - Built-in support for DAM integration and file uploads
📝 Page Selection - Native AEM page picker with content tree navigation
🧩 Content Fragments - Full support for Content Fragment and Experience Fragment pickers
✅ Validation - Regex patterns, min/max values, required fields, maxLength
💡 Contextual Help - Inline help tooltips and documentation links
🎨 Custom Styling - CSS classes, field width control, and Coral UI spacing
📝 Field Enhancements - Default values, descriptions, placeholder/emptyText
🎯 Enterprise Features - Custom IDs (granite:id), analytics tracking, render hidden, collapsible sections
🔍 Advanced Pickers - Filter support, freshness control for DAM assets
🔄 Multifield Pro - Delete confirmation, drag & drop reordering
🎯 Tracking - trackingElement para analytics granular (además de trackingFeature)
🧭 UX - autoFocus para enfocar campos al abrir el diálogo
🧱 Estilos - wrapperClass para clases del contenedor
🧪 Tipos - typeHint para guardar con el tipo correcto en JCR
🎨 Design Dialogs - Generate design dialogs (_cq_design_dialog) for component policies
📋 Policy Generation - Automatically create component policies with RTE config, styles, and more
🎨 Style System - Define style groups and variants for the AEM Style System
📐 Component Mapping - Configure asset-to-component drag & drop mappings
🔄 Dynamic Indentation - Consistent XML indentation across all generated content
⚡ Auto Style Tab - Automatic cq:styles tab generation when styleGroups are present
🔗 Template Integration - Automatic policy-to-template mapping for seamless deployment
Installation
npm install aem-dialog-generator-plugin --save-devQuick Start
1. Configure webpack
const AemDialogGeneratorPlugin = require('aem-dialog-generator-plugin');
const path = require('path');
module.exports = {
plugins: [
new AemDialogGeneratorPlugin({
sourceDir: path.resolve(__dirname, 'src/main/webpack/components'),
targetDir: path.resolve(__dirname, '../ui.apps/src/main/content/jcr_root/apps/mysite/components'),
appName: 'mysite',
generatePolicies: true,
policiesTargetDir: path.resolve(__dirname, '../ui.content/src/main/content/jcr_root/conf/mysite/settings/wcm/policies'),
templatePoliciesDir: path.resolve(__dirname, '../ui.content/src/main/content/jcr_root/conf/mysite/settings/wcm/templates'),
autoMapPoliciesToTemplates: true
})
]
};2. Create a dialog.json file
Create src/main/webpack/components/button/dialog.json:
{
"title": "Button Component",
"tabs": [
{
"title": "Properties",
"fields": [
{
"type": "textfield",
"name": "./text",
"label": "Button Text",
"required": true
},
{
"type": "pathfield",
"name": "./link",
"label": "Link",
"rootPath": "/content"
},
{
"type": "select",
"name": "./style",
"label": "Button Style",
"options": [
{ "value": "primary", "text": "Primary" },
{ "value": "secondary", "text": "Secondary" }
]
}
]
}
]
}3. Build
npm run devThe plugin automatically generates:
ui.apps/src/main/content/jcr_root/apps/mysite/components/button/_cq_dialog/.content.xmlConfiguration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| sourceDir | String | Required | Folder containing component dialog.json files |
| targetDir | String | Required | Target folder for generated XML files |
| dialogFileName | String | dialog.json | Name of the JSON configuration file |
| designDialogFileName | String | designDialog.json | Name of the design dialog JSON configuration file |
| appName | String | mysite | AEM application name |
| useFolderStructure | Boolean | true | Use _cq_dialog/.content.xml (true) or _cq_dialog.xml (false) |
| verbose | Boolean | false | Enable detailed logging |
| generatePolicies | Boolean | true | Enable automatic policy generation from designDialog.json |
| policiesTargetDir | String | ../ui.content/.../policies | Target folder for generated policy XML files |
| templatePoliciesDir | String | ../ui.content/.../templates | Target folder for template policy mapping |
| autoMapPoliciesToTemplates | Boolean | true | Automatically map policies to specified templates |
Supported Field Types
Basic Fields
textfield
{
"type": "textfield",
"name": "./title",
"label": "Title",
"description": "Enter title",
"required": true,
"defaultValue": "Default text"
}New Properties Quick Guide
typeHint (Sling/JCR)
Force the JCR data type saved by Sling. Useful for numbers, booleans, or arrays.
Examples:
{ "type": "textfield", "name": "./views", "label": "Views", "typeHint": "Long" }
{ "type": "select", "name": "./tags", "label": "Tags", "multiple": true, "typeHint": "String[]", "options": [{"value":"a","text":"A"}] }wrapperClass (field container)
Add CSS classes to the field container; merged with other Granite classes.
{ "type": "textfield", "name": "./title", "label": "Title", "className": "input-sm", "wrapperClass": "wrap-a wrap-b" }autoFocus (better UX)
Focus the field when the dialog opens.
{ "type": "textfield", "name": "./search", "label": "Search", "autoFocus": true }trackingElement (granular analytics)
Complement trackingFeature to identify a specific element interaction.
{ "type": "textfield", "name": "./ctaText", "label": "CTA Text", "trackingFeature": "hero", "trackingElement": "cta" }What's New in v2.0.0 🎉
Version 2.0.0 introduces powerful automation features that significantly reduce manual configuration and improve developer productivity:
🤖 Intelligent Automation
- Auto Style Tab: Automatically generates
cq:stylestab when policies containstyleGroups - Template Mapping: One-click policy deployment to multiple templates
- Dynamic Indentation: Perfect XML formatting regardless of complexity
🏗️ Enhanced Architecture
- Hierarchical Policies: Proper AEM policy directory structure (
/conf/{app}/settings/wcm/policies/{app}/components/) - Consistent XML: Unified XML generation using
buildNode()for reliability - Smart Detection: Automatically detects when style system integration is needed
🚀 Developer Experience
- Zero Configuration: Default settings work out of the box
- Automatic Deployment: Policies are instantly mapped to templates
- Production Ready: Generated XML follows all AEM best practices
📊 Comprehensive Testing
- 239 Tests: Complete test coverage including all new automation features
- Regression Protection: Ensures backward compatibility with existing configurations
- Edge Case Handling: Robust validation and error handling
Design Dialogs & Component Policies
The plugin now supports generating Design Dialogs (_cq_design_dialog) and their associated component policies. This simplifies the complex task of configuring component policies in AEM.
Creating a Design Dialog
Create a designDialog.json file alongside your dialog.json:
Example: src/main/webpack/components/button/designDialog.json
{
"title": "Button Design",
"layout": "simple",
"fields": [
{
"type": "checkbox",
"name": "./enableVariants",
"label": "Enable Button Variants",
"description": "Allow authors to choose between different button styles"
},
{
"type": "checkbox",
"name": "./enableSizes",
"label": "Enable Size Options"
}
],
"policy": {
"name": "policy_button",
"title": "Button Policy",
"description": "Policy configuration for button component",
"properties": {
"allowedVariants": "[primary,secondary,outline]",
"enableAnimation": "{Boolean}true"
},
"styleGroups": [
{
"name": "variants",
"label": "Button Variants",
"styles": [
{
"name": "primary",
"label": "Primary",
"classes": "cmp-button--primary"
},
{
"name": "secondary",
"label": "Secondary",
"classes": "cmp-button--secondary"
}
]
}
]
}
}Policy Configuration
The policy object in your designDialog.json supports:
Basic Policy Properties
{
"policy": {
"name": "policy_mycomponent",
"title": "My Component Policy",
"description": "Policy description shown in template editor",
"properties": {
"customProperty": "value",
"enableFeature": "{Boolean}true",
"maxItems": "{Long}5"
}
}
}Style System Configuration
Define style groups for the AEM Style System:
{
"policy": {
"styleGroups": [
{
"name": "layout",
"label": "Layout Options",
"styles": [
{
"name": "grid",
"label": "Grid Layout",
"classes": "cmp-container--grid",
"icon": "viewGrid"
},
{
"name": "flex",
"label": "Flex Layout",
"classes": "cmp-container--flex"
}
]
}
]
}
}RTE Plugin Configuration
Configure Rich Text Editor plugins in policies:
{
"policy": {
"rtePlugins": {
"format": {
"features": "bold,italic,underline"
},
"paraformat": {
"features": "*",
"formats": [
{ "description": "Heading 1", "tag": "h1" },
{ "description": "Heading 2", "tag": "h2" },
{ "description": "Paragraph", "tag": "p" }
]
},
"links": {
"features": "modifylink,unlink"
},
"lists": {
"features": "*"
},
"justify": {
"features": "-"
},
"table": {
"features": "-"
}
}
}
}Component Mapping
Configure asset-to-component drag & drop mappings:
{
"policy": {
"componentMapping": [
{
"assetGroup": "media",
"assetMimetype": "image/*",
"droptarget": "image",
"resourceType": "mysite/components/image"
},
{
"assetGroup": "content",
"assetMimetype": "text/html",
"droptarget": "experiencefragment",
"resourceType": "mysite/components/experiencefragment"
}
]
}
}Complete Design Dialog Example
Hero Component with Full Policy Configuration:
{
"title": "Hero Design",
"layout": "tabs",
"tabs": [
{
"title": "Layout Options",
"fields": [
{
"type": "select",
"name": "./defaultLayout",
"label": "Default Layout",
"defaultValue": "centered",
"options": [
{ "text": "Centered", "value": "centered" },
{ "text": "Left Aligned", "value": "left" },
{ "text": "Right Aligned", "value": "right" }
]
},
{
"type": "checkbox",
"name": "./allowBackgroundImage",
"label": "Allow Background Image"
}
]
}
],
"policy": {
"name": "policy_hero",
"title": "Hero Component Policy",
"description": "Comprehensive policy for hero component",
"properties": {
"allowedLayouts": "[centered,left,right]",
"minHeight": "{Long}400",
"maxHeight": "{Long}800"
},
"rtePlugins": {
"format": {
"features": "bold,italic"
},
"paraformat": {
"features": "*",
"formats": [
{ "description": "Heading 1", "tag": "h1" },
{ "description": "Paragraph", "tag": "p" }
]
},
"links": {
"features": "modifylink,unlink"
}
},
"styleGroups": [
{
"name": "layouts",
"label": "Hero Layouts",
"styles": [
{
"name": "centered",
"label": "Centered",
"classes": "cmp-hero--centered"
},
{
"name": "left",
"label": "Left Aligned",
"classes": "cmp-hero--left"
}
]
},
{
"name": "themes",
"label": "Color Themes",
"styles": [
{
"name": "light",
"label": "Light Theme",
"classes": "cmp-hero--light"
},
{
"name": "dark",
"label": "Dark Theme",
"classes": "cmp-hero--dark"
}
]
}
]
}
}Generated Output
When you build, the plugin generates:
- Design Dialog XML:
ui.apps/.../button/_cq_design_dialog/.content.xml - Policy XML:
ui.content/.../policies/button/.content.xml - Template Mapping: Automatically maps policies to specified templates (when
autoMapPoliciesToTemplates: true)
The policy XML is structured according to AEM standards and can be referenced in your page templates.
Automatic Features
The plugin now includes several automatic features to streamline AEM component development:
🎨 Automatic cq:styles Tab Generation
When a policy includes styleGroups, the plugin automatically generates a cq:styles tab in the design dialog:
{
"policy": {
"styleGroups": [
{
"name": "variants",
"label": "Button Variants",
"styles": [...]
}
]
}
}This automatically adds the AEM Style System tab to your design dialog without manual configuration.
🔗 Automatic Policy-to-Template Mapping
Configure which templates should use a component policy by adding a templates array to your policy:
{
"policy": {
"name": "policy_section_container",
"title": "Section Container Policy",
"description": "Policy for section component",
"templates": ["page-content", "landing-page"]
}
}The plugin will automatically update the template policy files to include the mapping:
<section
cq:policy="mysite/components/section/policy_section_container"
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/core/components/policies/mapping"/>📐 Dynamic Indentation System
All generated XML uses a consistent, dynamic indentation system that ensures properly formatted output regardless of nesting level or complexity.
📁 Correct Policy Structure
Policies are automatically generated in the proper AEM directory structure:
/conf/{appName}/settings/wcm/policies/{appName}/components/{componentName}/.content.xmlThis follows AEM best practices and ensures policies are properly organized.
textarea
{
"type": "textarea",
"name": "./description",
"label": "Description",
"rows": 5
}pathfield
{
"type": "pathfield",
"name": "./link",
"label": "Link",
"rootPath": "/content"
}select
{
"type": "select",
"name": "./type",
"label": "Type",
"options": [
{ "value": "type1", "text": "Type 1" },
{ "value": "type2", "text": "Type 2" }
]
}Advanced Properties
- Show/Hide by expression: use
showIforhideIfto emitgranite:hideand control field visibility. - Select datasource: add
datasource(child node),emptyOption,forceSelection. - Validation messages: override built-ins with
requiredMessage,minMessage,maxMessage,patternMessage. - Order fields: place with
orderBefore(emitssling:orderBefore). - Granite data: set
data: { key: value }→granite:data-key="value"on the field. - Render conditions:
renderConditionsupportssimple,privilege,and,orwith nested conditions. - Multifield UX:
addItemLabel,maxItemsMessage,minItemsMessage,reorderableHandle. - QoL inputs:
clearButton(textfield),autocomplete,ariaLabel,ariaDescribedBy,tooltipIcon.
Examples:
{
"type": "textfield",
"name": "./videoUrl",
"label": "Video URL",
"showIf": { "field": "./contentType", "value": "video" },
"required": true,
"requiredMessage": "Required for Video",
"ariaLabel": "Video URL"
}{
"type": "select",
"name": "./category",
"label": "Category",
"emptyOption": true,
"forceSelection": true,
"datasource": "/apps/mysite/datasources/categories"
}{
"type": "textfield",
"name": "./adminOnly",
"label": "Admin Only",
"renderCondition": {
"type": "and",
"conditions": [
{ "type": "simple", "expression": "${currentUser == 'admin'}" },
{ "type": "privilege", "privilege": "jcr:read" }
]
}
}{
"type": "multifield",
"name": "./items",
"label": "Items",
"addItemLabel": "Add Item",
"maxItemsMessage": "Too many",
"minItemsMessage": "Too few",
"reorderableHandle": "drag",
"fields": [{ "type": "textfield", "name": "./item", "label": "Item" }]
}checkbox
{
"type": "checkbox",
"name": "./enabled",
"label": "Enabled",
"text": "Enable feature",
"value": "true"
}numberfield
{
"type": "numberfield",
"name": "./count",
"label": "Count",
"min": 0,
"max": 100,
"step": 1
}datepicker
{
"type": "datepicker",
"name": "./date",
"label": "Date"
}colorfield
{
"type": "colorfield",
"name": "./color",
"label": "Color"
}switch
{
"type": "switch",
"name": "./active",
"label": "Active",
"checked": true
}hidden
{
"type": "hidden",
"name": "./hiddenValue",
"value": "hidden-value"
}fileupload
{
"type": "fileupload",
"name": "./fileReference",
"label": "File"
}hidden
{
"type": "hidden",
"name": "./componentId",
"defaultValue": "auto-generated-id"
}Stores values in JCR without displaying them in the dialog. Perfect for:
- Auto-generated IDs or timestamps
- Technical flags or metadata
- Calculated values set by JavaScript
- Values that authors shouldn't modify
Note: Hidden fields have no label or visual representation.
button
{
"type": "button",
"text": "Generate Content",
"variant": "primary",
"icon": "magic",
"command": "generateContent"
}Adds clickable buttons to trigger actions:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| text | String | Button label | "Button" |
| variant | String | Style: primary, secondary, action, warning | primary |
| icon | String | Coral UI icon name | - |
| command | String | Command to execute | - |
| handler | String | JavaScript handler file | - |
| type | String | Button type attribute | - |
| disabled | Boolean | Disable the button | false |
Use Cases:
- Trigger content generation
- Clear form fields
- Preview content
- Execute custom actions
- Integration with client libraries
Advanced Fields
fieldset - Group Related Fields
{
"type": "fieldset",
"label": "SEO Settings",
"fields": [
{
"type": "textfield",
"name": "./metaTitle",
"label": "Meta Title"
},
{
"type": "textarea",
"name": "./metaDescription",
"label": "Meta Description"
}
]
}container - Generic Container for Grouping
{
"type": "container",
"fields": [
{
"type": "textfield",
"name": "./option1",
"label": "Option 1"
},
{
"type": "textfield",
"name": "./option2",
"label": "Option 2"
}
]
}Differences:
- fieldset: Form-specific grouping with a visible label (
jcr:title). Requireslabelproperty. - container: Generic grouping element without visual label. The
nameproperty is optional (used only for node naming).
Note: Both support showhideClass for hiding entire groups of fields together.
fixedcolumns - Multi-Column Layout
{
"type": "fixedcolumns",
"columns": [
{
"fields": [
{ "type": "textfield", "name": "./firstName", "label": "First Name" },
{ "type": "textfield", "name": "./email", "label": "Email" }
]
},
{
"fields": [
{ "type": "textfield", "name": "./lastName", "label": "Last Name" },
{ "type": "textfield", "name": "./phone", "label": "Phone" }
]
}
]
}Organizes fields into side-by-side columns for better space utilization. Perfect for forms with related fields.
Properties:
| Property | Type | Description |
|----------|------|-------------|
| columns | Array | Array of column objects, each containing a fields array |
| name | String | Optional custom node name |
Column Properties:
name(String): Optional custom column name (default: column1, column2, etc.)fields(Array): Array of field definitions for this column
Common Use Cases:
- Name and contact info side by side
- Address fields in multiple columns
- Date ranges (From/To)
- Compact form layouts
well - Visual Grouping Container
{
"type": "well",
"name": "advancedSettings",
"fields": [
{ "type": "textfield", "name": "./cssClass", "label": "CSS Class" },
{ "type": "numberfield", "name": "./zIndex", "label": "Z-Index" },
{ "type": "checkbox", "name": "./customBehavior", "label": "Enable Custom Behavior" }
]
}A well is a container with a subtle gray background that visually groups related fields.
Properties:
| Property | Type | Description |
|----------|------|-------------|
| fields | Array | Array of field definitions to display in the well |
| name | String | Optional custom node name |
When to Use:
- Highlighting optional or advanced settings
- Visually separating field groups without tabs
- Drawing attention to important configuration sections
- Creating visual hierarchy within a tab
Visual Effect: Fields appear with a light gray background, creating a subtle "inset" appearance that groups them together.
heading - Section Heading
{
"type": "heading",
"text": "Advanced Settings",
"level": 3
}Creates a visual heading element to organize dialog sections. Does not store any data.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| text | String | The heading text to display (required) | - |
| level | Number | Heading level (1-6) | 3 |
text - Informational Text / Alert
{
"type": "text",
"text": "This setting will affect all child pages.",
"variant": "warning"
}Displays static informational text or alerts. Does not store any data. Also accepts "type": "alert" as an alias.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| text | String | The message to display (required) | - |
| variant | String | Visual style: info, warning, error, success | info |
Common Use Cases:
- Help text and instructions
- Warnings about field impacts
- Error messages or important notes
- Success confirmation messages
tags - AEM Tag Selector
{
"type": "tags",
"name": "./cq:tags",
"label": "Tags",
"required": true,
"rootPath": "/content/cq:tags/mysite"
}Provides an AEM tag picker that allows users to select from the AEM tagging system.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name where selected tags are stored (required) | - |
| label | String | Field label (required) | - |
| rootPath | String | Root path in tag hierarchy | /content/cq:tags |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Content categorization
- SEO keywords
- Content filtering and search
- Taxonomy management
image - Image Upload and Selection
{
"type": "image",
"name": "./image",
"label": "Image",
"required": true,
"uploadUrl": "/content/dam/mysite",
"allowUpload": true
}Provides an image upload field with DAM integration. Supports drag-and-drop, file browsing, and DAM asset selection.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| uploadUrl | String | Upload destination path in DAM | - |
| allowUpload | Boolean | Enable file upload | true |
| mimeTypes | Array | Allowed mime types | ['image/gif', 'image/jpeg', 'image/png', 'image/webp', 'image/tiff', 'image/svg+xml'] |
| fileNameParameter | String | Property for filename | ./fileName |
| fileReferenceParameter | String | Property for file reference | ./fileReference |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Hero images and banners
- Product images
- Author avatars
- Background images
autocomplete - Autocomplete Field
{
"type": "autocomplete",
"name": "./product",
"label": "Select Product",
"multiple": true,
"datasource": "/apps/mysite/datasources/products"
}Provides an autocomplete field with optional datasource integration for dynamic suggestions.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| datasource | String | Path to datasource for suggestions | - |
| multiple | Boolean | Allow multiple selections | false |
| forceSelection | Boolean | Only allow values from suggestions | true |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Product selection
- User search
- Category selection
- Dynamic value lists
radiogroup - Radio Button Group
{
"type": "radiogroup",
"name": "./layout",
"label": "Layout",
"vertical": false,
"options": [
{ "value": "grid", "text": "Grid" },
{ "value": "list", "text": "List", "checked": true }
]
}Displays a group of radio buttons. Better than select when you have 2-4 options that should be immediately visible.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| options | Array | Radio button options (required) | - |
| vertical | Boolean | Stack radio buttons vertically | false |
| defaultValue | Any | Default selected value | - |
| disabled | Boolean | Disable the field | false |
| required | Boolean | Make field mandatory | false |
Option Properties:
value(String): The value to storetext(String): Display textchecked(Boolean): Default selected option
Common Use Cases:
- Layout selection (2-3 options)
- Yes/No questions
- Content type selection
- Alignment options (left/center/right)
pagefield - AEM Page Selector
{
"type": "pagefield",
"name": "./targetPage",
"label": "Link to Page",
"required": true,
"rootPath": "/content/mysite/en"
}Provides an AEM-specific page selector with content tree navigation. Essential for any component that links to other pages.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| rootPath | String | Root path in content tree | /content |
| multiple | Boolean | Allow multiple page selection | false |
| filter | String | Filter node types or properties | - |
| pickerSrc | String | Custom picker dialog source | - |
| pickerTitle | String | Custom picker dialog title | - |
| pickerMultiselect | Boolean | Enable multiselect in picker | false |
| forceSelection | Boolean | Only allow values from picker | false |
| typeHint | String | JCR/Sling type hint | - |
| disabled | Boolean | Disable the field | false |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Navigation links
- Call-to-action buttons
- Related content links
- Breadcrumb configuration
- Footer links
contentfragmentpicker - Content Fragment Selector
{
"type": "contentfragmentpicker",
"name": "./fragmentPath",
"label": "Select Content Fragment",
"required": true,
"rootPath": "/content/dam/fragments",
"fragmentModel": "/conf/mysite/settings/dam/cfm/models/article"
}Provides a Content Fragment picker for selecting structured content. Essential for AEM headless and content-driven sites.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| rootPath | String | Root path in DAM | /content/dam |
| fragmentModel | String | Path to specific Content Fragment Model | - |
| multiple | Boolean | Allow multiple fragment selection | false |
| filter | String | Filter fragment types | - |
| pickerSrc | String | Custom picker dialog source | - |
| typeHint | String | JCR/Sling type hint | - |
| disabled | Boolean | Disable the field | false |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Article content references
- Product data integration
- Headless content delivery
- Structured data references
- Multi-channel content
experiencefragmentpicker - Experience Fragment Selector
{
"type": "experiencefragmentpicker",
"name": "./xfPath",
"label": "Select Experience Fragment",
"required": true,
"rootPath": "/content/experience-fragments/mysite"
}Provides an Experience Fragment picker for reusable component compositions. Perfect for headers, footers, and repeated content blocks.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| rootPath | String | Root path for XF | /content/experience-fragments |
| multiple | Boolean | Allow multiple XF selection | false |
| filter | String | Filter XF types | - |
| pickerSrc | String | Custom picker dialog source | - |
| typeHint | String | JCR/Sling type hint | - |
| disabled | Boolean | Disable the field | false |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Global headers and footers
- Reusable content blocks
- Multi-variant content
- Promotional banners
- Email templates
assetpicker - Generic Asset Selector
{
"type": "assetpicker",
"name": "./assetPath",
"label": "Select Asset",
"required": true,
"rootPath": "/content/dam/videos",
"mimeTypes": ["video/mp4", "video/webm", "application/pdf"]
}Provides a generic DAM asset picker with mime type filtering. More flexible than image for videos, documents, and other file types.
Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| rootPath | String | Root path in DAM | /content/dam |
| mimeTypes | Array | Allowed mime types | - |
| multiple | Boolean | Allow multiple asset selection | false |
| filter | String | Filter asset types | - |
| pickerSrc | String | Custom picker dialog source | - |
| pickerTitle | String | Custom picker dialog title | - |
| pickerMultiselect | Boolean | Enable multiselect in picker | false |
| forceSelection | Boolean | Only allow values from picker | false |
| typeHint | String | JCR/Sling type hint | - |
| disabled | Boolean | Disable the field | false |
| required | Boolean | Make field mandatory | false |
Common Use Cases:
- Video assets
- PDF documents
- Downloadable files
- Audio files
- Mixed media selection
rte - Rich Text Editor
{
"type": "rte",
"name": "./text",
"label": "Content",
"required": true,
"disabled": false,
"readOnly": false,
"height": "300px",
"width": "100%",
"maxlength": 5000,
"features": ["bold", "italic", "underline", "links", "lists"]
}Properties:
| Property | Type | Description | Default |
|----------|------|-------------|---------|
| name | String | Property name (required) | - |
| label | String | Field label (required) | - |
| required | Boolean | Make field mandatory | false |
| disabled | Boolean | Disable the editor | false |
| readOnly | Boolean | Make editor read-only | false |
| height | String | Editor height (CSS value) | - |
| width | String | Editor width (CSS value) | - |
| maxlength | Number | Maximum character count | - |
| useFixedInlineToolbar | Boolean | Use fixed inline toolbar | false |
| features | Array | Enabled RTE features | ['*'] |
Use "features": ["*"] for all features, or specify individual ones:
"bold","italic","underline"- Text formatting"links"- Hyperlinks"lists"- Ordered and unordered lists"justify"- Text alignment
Dynamic Show/Hide
The plugin supports AEM's built-in cq-dialog-dropdown-showhide and cq-dialog-checkbox-showhide scripts for conditional field visibility.
Dropdown Show/Hide
Show different fields based on dropdown selection:
{
"type": "select",
"name": "./contentType",
"label": "Content Type",
"cqShowHide": true,
"showhideTarget": ".content-fields",
"options": [
{
"text": "Image",
"value": "image"
},
{
"text": "Video",
"value": "video"
},
{
"text": "Text",
"value": "text"
}
]
}Then define containers that will be shown/hidden based on the selected value:
{
"type": "container",
"showhideClass": "content-fields",
"showhidetargetvalue": "image",
"fields": [
{
"type": "pathfield",
"name": "./imagePath",
"label": "Image Path",
"rootPath": "/content/dam"
},
{
"type": "textfield",
"name": "./altText",
"label": "Alt Text"
}
]
},
{
"type": "container",
"showhideClass": "content-fields",
"showhidetargetvalue": "video",
"fields": [
{
"type": "pathfield",
"name": "./videoUrl",
"label": "Video URL"
}
]
},
{
"type": "container",
"showhideClass": "content-fields",
"showhidetargetvalue": "text",
"fields": [
{
"type": "textarea",
"name": "./textContent",
"label": "Text Content"
}
]
}Checkbox Show/Hide
Show fields when checkbox is checked:
{
"type": "checkbox",
"name": "./enableCustomSettings",
"label": "Enable Custom Settings",
"cqShowHide": true,
"showhideTarget": ".custom-settings"
},
{
"type": "textfield",
"name": "./customValue",
"label": "Custom Value",
"showhideClass": "custom-settings"
},
{
"type": "numberfield",
"name": "./customNumber",
"label": "Custom Number",
"showhideClass": "custom-settings"
}Show/Hide Properties
| Property | Type | Used On | Description |
|----------|------|---------|-------------|
| cqShowHide | Boolean | select, checkbox | Enable show/hide functionality |
| showhideTarget | String | select, checkbox | CSS selector of elements to show/hide (e.g., ".my-fields") |
| showhideClass | String | fieldset, container | CSS class for elements that will be shown/hidden (e.g., "my-fields") |
| showhidetargetvalue | String | fieldset, container | Value that triggers showing this container (used with showhideClass) |
Note: You can use showhideClass on fieldset or container types to hide entire groups of fields together.
Generated XML for dropdown:
<contentType
granite:class="cq-dialog-dropdown-showhide"
...>
<granite:data
jcr:primaryType="nt:unstructured"
cq-dialog-dropdown-showhide-target=".content-fields"/>
<items jcr:primaryType="nt:unstructured">
<image text="Image" value="image"/>
<video text="Video" value="video"/>
<text text="Text" value="text"/>
</items>
</contentType>
<container_123456
sling:resourceType="granite/ui/components/coral/foundation/container"
granite:class="hide content-fields">
<granite:data
jcr:primaryType="nt:unstructured"
showhidetargetvalue="image"/>
<items jcr:primaryType="nt:unstructured">
<!-- image fields -->
</items>
</container_123456>Generated XML for checkbox:
<enableCustomSettings
granite:class="cq-dialog-checkbox-showhide"
...>
<granite:data
jcr:primaryType="nt:unstructured"
cq-dialog-checkbox-showhide-target=".custom-settings"/>
</enableCustomSettings>
<customValue
granite:class="hide custom-settings"
.../>Complete Show/Hide Example
{
"title": "Media Component",
"tabs": [
{
"title": "Content",
"fields": [
{
"type": "select",
"name": "./mediaType",
"label": "Media Type",
"cqShowHide": true,
"showhideTarget": ".media-fields",
"defaultValue": "image",
"options": [
{
"text": "Image",
"value": "image"
},
{
"text": "Video",
"value": "video"
}
]
},
{
"type": "container",
"showhideClass": "media-fields",
"showhidetargetvalue": "image",
"fields": [
{
"type": "pathfield",
"name": "./imageAsset",
"label": "Image Asset",
"rootPath": "/content/dam"
},
{
"type": "textfield",
"name": "./imageAlt",
"label": "Alt Text"
}
]
},
{
"type": "container",
"showhideClass": "media-fields",
"showhidetargetvalue": "video",
"fields": [
{
"type": "pathfield",
"name": "./videoAsset",
"label": "Video Asset",
"rootPath": "/content/dam"
},
{
"type": "checkbox",
"name": "./videoAutoplay",
"label": "Autoplay Video"
}
]
},
{
"type": "checkbox",
"name": "./addCaption",
"label": "Add Caption",
"cqShowHide": true,
"showhideTarget": ".caption-fields"
},
{
"type": "container",
"showhideClass": "caption-fields",
"fields": [
{
"type": "textarea",
"name": "./caption",
"label": "Caption Text"
}
]
}
]
}
]
}Hiding Groups of Fields with Container
You can use container or fieldset with showhideClass and showhidetargetvalue to hide multiple fields as a group:
{
"type": "select",
"name": "./mode",
"label": "Display Mode",
"cqShowHide": true,
"showhideTarget": ".mode-settings",
"options": [
{ "text": "Simple", "value": "simple" },
{ "text": "Advanced", "value": "advanced" }
]
},
{
"type": "container",
"name": "advancedSettings",
"showhideClass": "mode-settings",
"showhidetargetvalue": "advanced",
"fields": [
{
"type": "textfield",
"name": "./customClass",
"label": "Custom CSS Class"
},
{
"type": "numberfield",
"name": "./customWidth",
"label": "Custom Width"
},
{
"type": "textfield",
"name": "./dataAttributes",
"label": "Data Attributes"
}
]
}This will show/hide all three fields inside the container only when "Advanced" is selected. Note that container doesn't require a label - it's just a grouping element.
multifield - Repeatable Fields
Simple Multifield (single field repeated):
{
"type": "multifield",
"name": "./tags",
"label": "Tags",
"minItems": 1,
"maxItems": 5,
"fields": [
{
"type": "textfield",
"name": "./tag",
"label": "Tag"
}
]
}Composite Multifield (grouped fields repeated together):
{
"type": "multifield",
"name": "./slides",
"label": "Slides",
"composite": true,
"minItems": 2,
"maxItems": 10,
"fields": [
{
"type": "textfield",
"name": "./title",
"label": "Title",
"required": true
},
{
"type": "textarea",
"name": "./description",
"label": "Description"
},
{
"type": "pathfield",
"name": "./image",
"label": "Image",
"rootPath": "/content/dam"
}
]
}Multifield Properties:
| Property | Type | Description | Example |
|----------|------|-------------|---------|
| minItems | Number | Minimum number of items required (0 or greater) | minItems: 1 |
| maxItems | Number | Maximum number of items allowed (1 or greater) | maxItems: 10 |
| composite | Boolean | Group multiple fields together in each item | composite: true |
Conditional Tabs
Tabs can be shown or hidden based on the value of another field:
{
"title": "My Component",
"tabs": [
{
"title": "General",
"fields": [
{
"type": "checkbox",
"name": "./enableAdvanced",
"label": "Enable Advanced Features"
}
]
},
{
"title": "Advanced Settings",
"showIf": {
"field": "./enableAdvanced",
"value": true
},
"fields": [
{
"type": "textfield",
"name": "./customClass",
"label": "Custom CSS Class"
}
]
}
]
}The "Advanced Settings" tab will only appear when the "Enable Advanced Features" checkbox is checked.
showIf Properties:
field(String): Path to the field to check (e.g.,./enableAdvanced)value(Any): Value to compare against (true, false, "video", etc.)
Use Cases:
- Advanced/expert mode settings
- Content-type specific tabs (show video tab only when content type is "video")
- Optional feature configurations
- Progressive disclosure to simplify UX
Accordion Layout
Use layout: 'accordion' for collapsible sections instead of tabs:
{
"title": "My Component",
"layout": "accordion",
"tabs": [
{
"title": "Basic Settings",
"active": true,
"fields": [
{ "type": "textfield", "name": "./title", "label": "Title" },
{ "type": "textarea", "name": "./description", "label": "Description" }
]
},
{
"title": "Advanced Options",
"fields": [
{ "type": "textfield", "name": "./cssClass", "label": "CSS Class" },
{ "type": "numberfield", "name": "./order", "label": "Display Order" }
]
}
]
}Accordion Properties:
| Property | Type | Description |
|----------|------|-------------|
| layout | String | Set to "accordion" to use collapsible sections |
| tabs | Array | Array of section objects (reuses tabs structure) |
Section Properties:
title(String): Section headingactive(Boolean): Iftrue, this section is expanded by default (default:false)fields(Array): Fields within this sectionname(String): Optional custom node name
When to use Accordion vs Tabs:
- Accordion: Many sections, progressive disclosure, or when authors need to see multiple sections at once
- Tabs: Few sections (2-5), mutually exclusive content, or when a cleaner single-view interface is preferred
Field Descriptions
Add helpful guidance text below any field using the description property:
{
"type": "textfield",
"name": "./title",
"label": "Title",
"description": "Enter a short, descriptive title for this component",
"required": true
}The description appears below the field in a lighter font, providing context and instructions to content authors.
Placeholder Text
Add example text inside empty fields using the placeholder property:
{
"type": "textfield",
"name": "./username",
"label": "Username",
"placeholder": "Enter your username"
}Placeholder text appears in a lighter color inside empty fields and disappears when the user starts typing. Very useful for:
- Showing format examples ("MM/DD/YYYY")
- Providing input hints ("Type to search...")
- Clarifying expected values ("e.g., [email protected]")
Supported on: textfield, textarea, numberfield, pathfield, select, autocomplete
Min/Max Validation
Set numeric range constraints on number fields:
{
"type": "numberfield",
"name": "./age",
"label": "Age",
"min": 18,
"max": 99,
"placeholder": "Enter age between 18-99"
}The min and max properties enforce numeric boundaries:
min(Number): Minimum allowed value (inclusive)max(Number): Maximum allowed value (inclusive)- Works with
numberfieldtype - Browser-native validation
Common Use Cases:
// Percentage (0-100)
{ "type": "numberfield", "name": "./opacity", "label": "Opacity %", "min": 0, "max": 100 }
// Positive numbers only
{ "type": "numberfield", "name": "./quantity", "label": "Quantity", "min": 1 }
// Rating system
{ "type": "numberfield", "name": "./rating", "label": "Rating", "min": 1, "max": 5 }Disabled and ReadOnly Fields
Control field interactivity:
// Disabled field - grayed out, not submitted
{
"type": "textfield",
"name": "./calculatedValue",
"label": "Calculated Value",
"disabled": true,
"defaultValue": "Auto-generated"
}
// ReadOnly field - visible but not editable, submitted with form
{
"type": "textfield",
"name": "./timestamp",
"label": "Created Date",
"readOnly": true,
"defaultValue": "2024-01-15"
}Properties:
disabled(Boolean): Disables the field completely (default:false)readOnly(Boolean): Makes field read-only but still submits value (default:false)
Key Differences:
- Disabled: Field is grayed out and value is NOT submitted
- ReadOnly: Field looks normal but can't be edited, value IS submitted
Use Cases:
- Disabled: Conditional fields, insufficient permissions, calculated values
- ReadOnly: System-generated IDs, timestamps, inherited values, display-only info
Multiple Selection
Allow selecting multiple options in dropdowns:
{
"type": "select",
"name": "./categories",
"label": "Categories",
"multiple": true,
"options": [
{ "text": "News", "value": "news" },
{ "text": "Events", "value": "events" },
{ "text": "Blog", "value": "blog" }
]
}The multiple property enables multi-select:
- Works on
selectandautocompletefield types - Users can select multiple values using Ctrl/Cmd + click
- Values are stored as an array
Common Use Cases:
- Tag selection
- Category assignment
- Permission selection
- Feature toggles
Contextual Help
Add help icon with tooltip next to field labels:
// Simple text tooltip
{
"type": "textfield",
"name": "./pattern",
"label": "RegEx Pattern",
"contextualHelp": "Enter a valid JavaScript regular expression"
}
// With external documentation link
{
"type": "select",
"name": "./layout",
"label": "Layout Type",
"contextualHelp": {
"text": "Choose the layout format for this component",
"url": "https://docs.example.com/layouts"
},
"options": [...]
}The contextualHelp property adds a small ⓘ icon next to the field label:
- String value: Shows tooltip on hover
- Object value: Shows tooltip and optional "Learn more" link
text(String): Tooltip contenturl(String): External documentation URL
Benefits:
- Keeps UI clean while providing detailed help
- Reduces need for lengthy field descriptions
- Links to external documentation for complex features
- Standard AEM pattern for contextual assistance
Custom CSS Classes
Add custom CSS classes to any field using the className property:
// Single class
{
"type": "textfield",
"name": "./title",
"label": "Title",
"className": "custom-field-style"
}
// Multiple classes (string with spaces)
{
"type": "select",
"name": "./type",
"label": "Type",
"className": "highlight-field required-indicator",
"options": [...]
}
// Multiple classes (array format)
{
"type": "textarea",
"name": "./description",
"label": "Description",
"className": ["large-textarea", "rich-editor"]
}The className property allows you to:
- Apply custom styling to specific fields
- Add JavaScript selector hooks for custom behavior
- Highlight important or required fields visually
- Maintain consistent styling across similar fields
Note: Custom classes are merged with existing Granite UI classes (like show/hide classes) and added to the field's granite:class attribute.
Field Width Control
Control the width of individual fields using the width property:
// Fixed pixel width
{
"type": "textfield",
"name": "./code",
"label": "Product Code",
"width": "150px"
}
// Percentage width
{
"type": "numberfield",
"name": "./quantity",
"label": "Qty",
"width": "30%"
}
// Numeric value (treated as pixels)
{
"type": "select",
"name": "./size",
"label": "Size",
"width": "200",
"options": [...]
}The width property accepts:
- Pixel values: "100px", "250px"
- Percentages: "50%", "75%"
- Numeric strings: "200" (treated as pixels)
- CSS values: "auto", "fit-content"
Common Use Cases:
- Short input fields for codes, IDs, or numbers
- Compact layouts with multiple fields per row
- Consistent sizing across related fields
- Optimizing dialog space utilization
Coral UI Spacing (Margin)
Control vertical spacing between fields using the margin property:
// Add margin above field
{
"type": "heading",
"text": "Advanced Settings",
"level": 3,
"margin": true
}
// No margin (tight layout)
{
"type": "textfield",
"name": "./field1",
"label": "Field 1",
"margin": false
}
// Default behavior (don't specify)
{
"type": "textfield",
"name": "./field2",
"label": "Field 2"
}The margin property controls Coral UI's vertical spacing:
true: Adds standard margin above the fieldfalse: Removes margin for tight layoutsundefined: Uses Coral UI default spacing
When to Use:
margin: true: Add visual separation before headings or new sectionsmargin: false: Create compact, dense layouts; group tightly related fields- Default: Most fields should use default Coral spacing
Common Patterns:
// Section with visual breathing room
{
"type": "heading",
"text": "Section Title",
"margin": true
},
// Compact field group (address fields, name parts, etc.)
{
"type": "textfield",
"name": "./street1",
"label": "Street Address",
"margin": false
},
{
"type": "textfield",
"name": "./street2",
"label": "Apt/Suite",
"margin": false
}Default Values
Set initial values for fields using the defaultValue property:
// Text field with default
{
"type": "textfield",
"name": "./title",
"label": "Title",
"defaultValue": "Welcome Message"
}
// Number field with default
{
"type": "numberfield",
"name": "./quantity",
"label": "Quantity",
"defaultValue": 1,
"min": 1
}
// Checkbox with default checked state
{
"type": "checkbox",
"name": "./enabled",
"label": "Enable Feature",
"defaultValue": true
}
// Select with pre-selected option
{
"type": "select",
"name": "./theme",
"label": "Theme",
"defaultValue": "dark",
"options": [
{ "value": "light", "text": "Light" },
{ "value": "dark", "text": "Dark" }
]
}The defaultValue property:
- Sets the initial value when component is first added
- Works with all field types (textfield, numberfield, checkbox, select, etc.)
- Values are stored in the
valueattribute in the XML - Useful for sensible defaults that authors can modify
Common Use Cases:
- Default "Read More" text for CTAs
- Pre-set quantity to 1 in product components
- Enable features by default (opt-out vs opt-in)
- Default theme or style selection
Field Descriptions
Add helpful descriptive text below field labels using the description property:
{
"type": "textfield",
"name": "./email",
"label": "Email Address",
"description": "This email will be used for notification purposes only"
}
{
"type": "pathfield",
"name": "./backgroundImage",
"label": "Background Image",
"description": "Recommended size: 1920x1080px. Supports JPG, PNG, and WebP formats.",
"rootPath": "/content/dam"
}
{
"type": "numberfield",
"name": "./animationDuration",
"label": "Animation Duration",
"description": "Duration in milliseconds. Lower values = faster animations.",
"min": 100,
"max": 5000,
"defaultValue": 1000
}The description property:
- Adds
fieldDescriptionattribute in AEM - Appears as gray text below the field label
- Helps authors understand field purpose and constraints
- Supports special characters (automatically escaped)
When to Use:
- Explaining technical fields (CSS classes, regex patterns)
- Providing examples or format requirements
- Clarifying business rules or constraints
- Guiding authors on best practices
Pro Tip: Combine with contextualHelp for comprehensive guidance:
{
"type": "textfield",
"name": "./regex",
"label": "Validation Pattern",
"description": "JavaScript regex pattern for validation",
"contextualHelp": {
"text": "Enter a valid JavaScript regular expression",
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions"
}
}Maximum Length Validation
Limit the number of characters users can enter using the maxLength property:
// Short text field
{
"type": "textfield",
"name": "./title",
"label": "Title",
"maxLength": 50,
"placeholder": "Maximum 50 characters"
}
// Textarea with character limit
{
"type": "textarea",
"name": "./description",
"label": "Description",
"maxLength": 500,
"rows": 5,
"description": "Maximum 500 characters"
}
// Product code
{
"type": "textfield",
"name": "./sku",
"label": "SKU",
"maxLength": 12,
"placeholder": "12-char code"
}The maxLength property:
- Adds
maxlengthattribute to the field - Prevents users from entering more than the specified number of characters
- Browser-native validation (no form submission required)
- Works with
textfield,textarea, andpathfieldtypes
Common Use Cases:
// Tweet-length content
{ "maxLength": 280 }
// Meta descriptions for SEO
{ "maxLength": 160 }
// Headline/Title fields
{ "maxLength": 100 }
// Product codes or IDs
{ "maxLength": 20 }Best Practices:
- Always inform users about the limit (via description or placeholder)
- Consider UX: extremely restrictive limits can frustrate authors
- Use in combination with required validation for data quality
- Common limits: 50 (titles), 160 (meta), 500 (short descriptions), 2000 (long content)
Empty Text
Provide placeholder text using the emptyText property (Coral UI native alternative to placeholder):
// Search field
{
"type": "textfield",
"name": "./search",
"label": "Search",
"emptyText": "Type to search..."
}
// Email with format example
{
"type": "textfield",
"name": "./email",
"label": "Email",
"emptyText": "[email protected]"
}
// Date format hint
{
"type": "textfield",
"name": "./eventDate",
"label": "Event Date",
"emptyText": "MM/DD/YYYY"
}The emptyText property:
- Native Coral UI property (semantic alternative to placeholder)
- Displays hint text in empty fields
- Disappears when user starts typing
- If both
placeholderandemptyTextare specified,emptyTexttakes precedence
Difference from placeholder:
placeholder→ Converted toemptyTextin AEM (for backward compatibility)emptyText→ Direct Coral UI property (more semantic)- Both achieve the same visual result
- Use
emptyTextfor new implementations,placeholderfor familiarity
Common Patterns:
// Format hints
{ "emptyText": "YYYY-MM-DD" }
{ "emptyText": "(555) 123-4567" }
// Action prompts
{ "emptyText": "Start typing..." }
{ "emptyText": "Select an option" }
// Examples
{ "emptyText": "e.g., John Doe" }
{ "emptyText": "e.g., /content/mysite/en" }Pro Tip: For complex format requirements, combine with description or validation:
{
"type": "textfield",
"name": "./phone",
"label": "Phone Number",
"emptyText": "(555) 123-4567",
"description": "US phone numbers only",
"validation": {
"pattern": "^\\(\\d{3}\\) \\d{3}-\\d{4}$",
"message": "Please use format: (555) 123-4567"
}
}
### Granite ID (Custom Field IDs)
Assign custom IDs to fields for JavaScript integration and specific styling using the `graniteId` property:
```json
// Custom field ID for JavaScript hooks
{
"type": "select",
"name": "./contentType",
"label": "Content Type",
"graniteId": "content-type-selector",
"options": [...]
}
// For dynamic field manipulation
{
"type": "textfield",
"name": "./dynamicField",
"label": "Dynamic Field",
"graniteId": "js-dynamic-field"
}
// Multiple fields with coordinated IDs
{
"type": "checkbox",
"name": "./enableAdvanced",
"label": "Enable Advanced",
"graniteId": "advanced-toggle",
"cqShowHide": true,
"showhideTarget": ".advanced-options"
},
{
"type": "numberfield",
"name": "./advancedValue",
"label": "Advanced Value",
"graniteId": "advanced-input",
"showhideClass": "advanced-options"
}The graniteId property:
- Adds
granite:idattribute to the field - Provides stable, predictable IDs for JavaScript selectors
- Useful for custom client libraries and interactions
- Better than relying on generated or name-based IDs
Common Use Cases:
- Custom JavaScript validation or formatting
- Integration with third-party libraries
- Dynamic field behavior (calculations, cascading dropdowns)
- Automated testing with stable selectors
- CSS styling for specific fields
Best Practices:
// Use descriptive, kebab-case IDs
{ "graniteId": "hero-title-input" }
{ "graniteId": "primary-cta-link" }
// Prefix by component or feature
{ "graniteId": "carousel-slide-count" }
{ "graniteId": "video-autoplay-toggle" }
// Avoid generic names
// ❌ Bad: "field1", "input", "select"
// ✅ Good: "product-sku", "author-bio", "featured-image"Tracking Feature (Analytics Integration)
Add analytics tracking identifiers to fields using the trackingFeature property:
// Track field usage in Adobe Analytics
{
"type": "select",
"name": "./template",
"label": "Template Selection",
"trackingFeature": "template-selector",
"options": [...]
}
// Track feature toggles
{
"type": "checkbox",
"name": "./enableVideo",
"label": "Enable Video Background",
"trackingFeature": "video-background-toggle"
}
// Track specific component interactions
{
"type": "pathfield",
"name": "./ctaLink",
"label": "CTA Link",
"trackingFeature": "hero-cta-link",
"rootPath": "/content"
}The trackingFeature property:
- Adds
trackingFeatureattribute for analytics frameworks - Enables tracking of component configuration patterns
- Helps identify which features are most used by authors
- Integrates with Adobe Analytics or custom tracking solutions
Enterprise Use Cases:
// Component adoption tracking
{
"trackingFeature": "layout-grid-usage"
}
// Feature flag analysis
{
"trackingFeature": "personalization-enabled"
}
// Content strategy insights
{
"trackingFeature": "video-content-type"
}
// A/B testing configurations
{
"trackingFeature": "variation-b-selected"
}Analytics Dashboard Examples:
- Most configured component features
- Template selection trends
- Feature adoption rates
- Author workflow patterns
Render Hidden
Conditionally hide fields in the UI while preserving their functionality using the renderHidden property:
// Hidden until condition met
{
"type": "textfield",
"name": "./apiKey",
"label": "API Key",
"renderHidden": true,
"description": "Only shown to administrators"
}
// Programmatically revealed field
{
"type": "numberfield",
"name": "./advancedSetting",
"label": "Advanced Setting",
"renderHidden": true
}
// Combined with conditional logic
{
"type": "select",
"name": "./mode",
"label": "Mode",
"options": [
{ "value": "simple", "text": "Simple" },
{ "value": "advanced", "text": "Advanced" }
]
},
{
"type": "textfield",
"name": "./advancedConfig",
"label": "Advanced Config",
"renderHidden": true,
"description": "Revealed when Advanced mode is selected"
}The renderHidden property:
- Adds
renderHidden="{Boolean}true"attribute - Hides field in dialog UI but keeps it in DOM
- Different from
type: "hidden"(which never renders) - Can be shown/hidden dynamically with JavaScript
- Field still submits its value when form is saved
**Difference from `type
