返回 Skills
adobe/skills· Apache-2.0 内容可用

ue-component-model

Create or edit the Universal Editor component configuration (component-definition.json, component-models.json, component-filters.json) for AEM Edge Delivery Services blocks. Use this skill whenever the user mentions component models, component definitions, component filters, block configuration for the Universal Editor, UE block setup, adding a new block to UE, configuring block properties, block authoring fields, or any task involving the three JSON config files that control how blocks appear in the Universal Editor. Also trigger when the user wants to create a new EDS/Franklin block with UE support, modify block fields, add a block to the section filter, or asks about how blocks connect to the Universal Editor.

安装

与 skills.sh 相同的 Command / Prompt 安装方式


name: ue-component-model description: Create or edit the Universal Editor component configuration (component-definition.json, component-models.json, component-filters.json) for AEM Edge Delivery Services blocks. Use this skill whenever the user mentions component models, component definitions, component filters, block configuration for the Universal Editor, UE block setup, adding a new block to UE, configuring block properties, block authoring fields, or any task involving the three JSON config files that control how blocks appear in the Universal Editor. Also trigger when the user wants to create a new EDS/Franklin block with UE support, modify block fields, add a block to the section filter, or asks about how blocks connect to the Universal Editor. license: Apache-2.0

Universal Editor Component Model Configuration

This skill helps you create or edit the three JSON configuration files that control how AEM Edge Delivery Services (EDS) blocks appear and behave in the Universal Editor (UE):

  1. component-definition.json — Registers blocks in the UE component palette
  2. component-models.json — Defines property panel fields for each block
  3. component-filters.json — Controls where blocks can be placed

When to Use

  • Creating a new block that needs UE authoring support
  • Adding/modifying fields on an existing block's property panel
  • Registering a block so it appears in the author's component palette
  • Setting up container blocks with child items
  • Adding block variants/style options

Workflow

Step 1: Understand the Block

Before generating any configuration, read and analyze:

  1. The block's JS file (blocks/<name>/<name>.js) — understand what content the decorate(block) function expects:

    • What does it read from the block div? (images, links, text, classes)
    • Does it expect a flat structure or rows of items?
    • Does it use block.querySelector('a') (links/URLs), block.querySelector('picture') (images), etc.?
    • Does it check for CSS classes/variants?
  2. The block's CSS file (blocks/<name>/<name>.css) — look for variant-specific styles.

  3. Existing config — check if entries already exist:

    • Search component-definition.json for the block ID
    • Search component-models.json for the model ID
    • Search component-filters.json for the block in the section components list
    • Check for a blocks/<name>/_<name>.json distributed config file

Step 2: Determine the Block Type

Based on the JS analysis:

  • Simple block: One component with its own fields. Most blocks are this type.

    • Example: Hero, Embed — single model, no children
  • Container block: Has repeatable child items (cards, slides, tabs).

    • Clue: JS iterates over block.children or creates items from rows
    • Needs: container definition + item definition + filter
  • Key-value block: Configuration-style block (2-column key-value pairs).

    • Clue: Each property is independent, not a grid of content
    • Needs: "key-value": true in template

Step 3: Design the Model Fields

Map the block's content expectations to component model fields. Read references/field-types.md for the full field type reference.

Common field mappings:

Block expects...Use component typeNotes
An imagereference (name: image)Pair with text field named imageAlt
A URL/linkaem-content (name: link or url)For page links and external URLs
Rich text contentrichtextFor formatted text with headings, lists, links
Plain text (single line)textFor titles, labels, short strings
Plain text (multi-line)textareaFor descriptions, notes, long text without formatting
Heading level choiceselect with h1-h6 optionsName it titleType to auto-collapse with title
Style variantsmultiselect (name: classes)Values become CSS classes on block div
Multiple togglescheckbox-groupFor multiple independent boolean options
Boolean togglebooleanFor show/hide options
Number valuenumberFor counts, limits
Content Fragmentaem-content-fragmentFor CF-driven blocks
Experience Fragmentaem-experience-fragmentFor reusable content+layout fragments
Content tagsaem-tagFor categorization via AEM tag picker

Field naming rules (semantic collapsing):

  • image + imageAlt → collapsed into <picture><img alt="...">
  • link + linkText + linkTitle + linkType → collapsed into <a href="..." title="...">text</a> with optional class
  • title + titleType → collapsed into <h2>title</h2> (level from titleType)
  • Fields prefixed with group_ (underscore separator) are grouped into a single cell

Step 4: Generate the Configuration

Generate entries for all three files. The approach depends on whether the project uses centralized or distributed config.

Check for distributed config pattern: If the block directory contains _<blockname>.json files (e.g., blocks/hero/_hero.json), create a distributed config file instead of editing the central files.

For Centralized Config (editing the three root JSON files):

component-definition.json — Add to the "Blocks" group's components array:

{
  "title": "<Block Display Name>",
  "id": "<block-id>",
  "plugins": {
    "xwalk": {
      "page": {
        "resourceType": "core/franklin/components/block/v1/block",
        "template": {
          "name": "<Block Name>",
          "model": "<model-id>"
        }
      }
    }
  }
}

For container blocks, add both the container AND item definitions. The container gets "filter" instead of "model", and the item uses "core/franklin/components/block/v1/block/item" as resourceType.

For key-value blocks, add "key-value": true to the template.

Template can include default values for any model field (e.g., "titleType": "h3", "classes": ["light"]).

component-models.json — Add a new model entry:

{
  "id": "<model-id>",
  "fields": [
    {
      "component": "<field-type>",
      "name": "<property-name>",
      "label": "<Display Label>",
      "valueType": "string"
    }
  ]
}

component-filters.json — Add the block ID to the section filter's components array. For container blocks, also add a new filter entry defining allowed children.

For Distributed Config (creating blocks/<name>/_<name>.json):

Create a single file with all three configs:

{
  "definitions": [ ... ],
  "models": [ ... ],
  "filters": [ ... ]
}

Still add the block to the section filter in the central component-filters.json.

Step 5: Validate

After generating the config, verify:

  1. ID consistency: The id in the definition matches what's used in component-filters.json. The template.model value matches the id in component-models.json.
  2. Filter registration: The block's ID appears in the section filter's components array (otherwise authors can't add it to pages).
  3. Field names match block JS: The name properties in the model fields should produce HTML that the block's decorate() function can consume.
  4. Semantic collapsing: Paired fields use correct suffixes (e.g., image/imageAlt, not image/altText unless intentional).
  5. Valid JSON: All three files remain valid JSON after edits.
  6. No duplicate IDs: No model or filter ID conflicts with existing entries.

Reference Files

For detailed information, read these reference files as needed:

  • references/architecture.md — How the three files connect, the full AEM→Markdown→HTML pipeline, resource types, field naming conventions, semantic collapsing rules, and RTE filter configuration
  • references/field-types.md — Complete reference for all 17 field component types (text, textarea, richtext, reference, aem-content, aem-content-fragment, aem-experience-fragment, aem-tag, select, multiselect, checkbox-group, radio-group, boolean, number, date-time, container, tab), valueType constraints, required properties, field properties, validation types, conditional fields, and option formats
  • references/examples.md — Real examples showing Hero (simple), Embed (simple with URL), Cards (container), Teaser (variants), Product Details (key-value), Article (content fragment), Section configuration, Metadata (textarea), Feature Toggles (checkbox-group), and RTE filter configuration

Common Pitfalls

  • Forgetting to add to section filter: The block won't appear in the author's add menu unless it's in the section filter's components list.
  • Wrong resourceType: Almost all custom blocks use core/franklin/components/block/v1/block. Don't invent custom resource types.
  • Mismatched model/filter IDs: The template.model must exactly match the model id, and template.filter must exactly match the filter id.
  • Choosing the wrong text field type: Use text for single-line strings, textarea for multi-line plain text, and richtext for formatted content. For URLs and page links, use aem-content so authors get the content picker.
  • Wrong valueType: Most components enforce a specific valueType (e.g., boolean must use "boolean", number must use "number", checkbox-group must use "string[]"). Always include valueType and check the field-types reference for the enforced value.
  • Container without filter: Container blocks need a filter (not a model) in their template, and a corresponding filter entry in component-filters.json.

附带文件

package.json
{
  "name": "ue-component-model",
  "version": "0.0.0-semantically-released",
  "private": true
}
references/architecture.md
# Architecture: How the Three JSON Files Connect

## The Three Files

An AEM Edge Delivery Services (aem.live) project using the Universal Editor (UE) requires three JSON configuration files at the project root:

1. **component-definition.json** — Registers components in the UE palette (what authors can add)
2. **component-models.json** — Defines the property panel fields (what authors can configure)
3. **component-filters.json** — Controls component hierarchy (where components can be placed)

## How They Connect

```
component-definition.json          component-models.json         component-filters.json
┌──────────────────────┐          ┌─────────────────────┐       ┌──────────────────────┐
│ {                    │          │ {                   │       │ {                    │
│   "id": "hero",      │          │   "id": "hero",     │       │   "id": "section",   │
│   "template": {      │          │   "fields": [...]   │       │   "components": [    │
│     "model": "hero"──┼─────────►│ }                   │       │     "hero",          │
│     "filter": "cards"┼──────┐   └─────────────────────┘       │     "cards", ...     │
│   }                  │      │                                  │   ]                 │
│ }                    │      │                                  │ }                   │
└──────────────────────┘      │   ┌─────────────────────┐       │ {                    │
                              └──►│ "id": "cards",      │       │   "id": "cards",     │
                                  │ "components":["card"]│◄─────┤ }                    │
                                  └─────────────────────┘       └──────────────────────┘
```

### Link 1: `template.model` → `component-models.json` id
The `model` property in a component definition's template references a model `id` in component-models.json. When an author selects a component, the UE renders the matching model's `fields` as the property panel.

### Link 2: `template.filter` → `component-filters.json` id
Container components (like Cards) use a `filter` to restrict which child components can be added inside them. The filter `id` in component-filters.json lists the allowed `components` array.

### Link 3: Section filter lists block IDs
The `section` filter in component-filters.json lists all block IDs that can be added to a page section. New blocks MUST be added here to appear in the author's "add component" menu.

## Full Pipeline: AEM → Markdown → HTML → Block JS

```
1. Author creates content in Universal Editor
   ├── component-definition.json tells UE what components exist
   ├── component-models.json tells UE what fields to show
   └── component-filters.json tells UE where components can go
           │
2. AEM stores content as JCR nodes
   ├── resourceType determines the rendering component
   ├── template properties become JCR properties
   └── Block.java renders HTML table structure
           │
3. helix-html2md converts AEM HTML → Markdown
   ├── Block div with classes → Grid table with header row
   ├── Block name from first class (title-cased)
   └── Variants from additional classes → parenthetical "(variant)"
           │
4. helix-html-pipeline converts Markdown → HTML
   ├── Grid table → block div with CSS classes
   ├── Block name → lowercase kebab-case class
   └── Each row → nested div, each cell → inner div
           │
5. Block JS decorates the HTML
   ├── blocks/<name>/<name>.js receives the block div
   ├── decorate(block) reads content from the div structure
   └── Transforms into interactive UI
```

## Block Types

### Simple Block (most common)
A block with a single model, no children. Example: Hero, Embed.
- **definition**: 1 entry with `model` reference
- **model**: 1 entry with fields
- **filter**: Just add block ID to `section` components list

### Container Block
A block that contains repeatable child items. Example: Cards.
- **definition**: 2 entries (container + item). Container has `filter`, item has `model`
- **model**: 1 entry for the item
- **filter**: 1 new filter defining allowed children, plus add container to `section` list

### Key-Value Block
A block rendered as key-value pairs (2-column table). Example: Product Details, Commerce blocks.
- **definition**: 1 entry with `"key-value": true` in template
- **model**: 1 entry with fields
- **filter**: Add to `section` list

## Resource Types

Almost all blocks use one of these:
- `core/franklin/components/block/v1/block` — Standard block (most blocks)
- `core/franklin/components/block/v1/block/item` — Child item within a container block
- `core/franklin/components/section/v1/section` — Section component
- `core/franklin/components/text/v1/text` — Default text
- `core/franklin/components/title/v1/title` — Title
- `core/franklin/components/image/v1/image` — Image
- `core/franklin/components/button/v1/button` — Button
- `core/franklin/components/columns/v1/columns` — Columns (special)

Custom blocks should use `core/franklin/components/block/v1/block`. Do NOT create custom resource types.

## Field Name Conventions & Semantic Collapsing

The pipeline uses naming suffixes to collapse related fields into single semantic elements:

| Suffix | Behavior | Example |
|--------|----------|---------|
| `Alt` | Image alt text | `imageAlt` collapses into `image` as `<img alt="...">` |
| `Text` | Link display text | `linkText` collapses into `link` as `<a>text</a>` |
| `Title` | Link/heading title | `linkTitle` collapses into `link` as `<a title="...">` |
| `Type` | Heading level or link style | `titleType` → `<h2>`, `linkType` → class on `<a>` |
| `MimeType` | File type hint | `fileMimeType` → type attribute |

**Element Grouping**: Fields sharing a prefix with `_` separator are grouped into a single table cell:
- `cta_link`, `cta_text`, `cta_type` → grouped in one cell

**Block Options (`classes`)**: The `classes` field becomes CSS classes on the block wrapper:
- `"classes": ["light", "left"]` → `<div class="teaser light left">`
- Boolean sub-options: `classes_fullwidth: true` → adds `fullwidth` class

## Type Inference

The pipeline auto-detects content types:
- MIME type starting with `image/` → `<picture><img src="...">`
- Values matching URL patterns (`https://`, `#`) → `<a href="...">`
- Values starting with HTML tags → rendered as rich text
- Multi-value properties → comma-separated or `<ul><li>` lists

## RTE Filter Configuration

Filters can include an `rte` property to configure the richtext editor toolbar for components using that filter. This controls which formatting options are available to authors when editing richtext fields.

### Toolbar Categories

The `toolbar` property organizes actions into category groups:

| Category | Available actions | Notes |
|----------|------------------|-------|
| `format` | `bold`, `italic`, `underline`, `strike` / `strikethrough` | `strike` and `strikethrough` are aliases. Optional `tag` property overrides HTML element. |
| `blocks` | `h1`-`h6`, `paragraph`, `code_block` | Block-level formatting |
| `list` | `bullet_list` / `bullist`, `ordered_list` / `numlist` | Aliases: `bullet_list`=`bullist`, `ordered_list`=`numlist`. Optional `wrapInParagraphs`. |
| `insert` | `link`, `table`, `image`, `unlink` | `link` supports `disableForImages`, `hideTarget`. `image` supports `wrapInPicture`. |
| `alignment` | `left`, `right`, `center`, `justify`, `alignleft`, `alignright`, `aligncenter`, `alignjustify` | Short and prefixed forms are both valid |
| `indentation` | `indent`, `outdent` | Block indentation |
| `sr_script` | `superscript`, `subscript` | Superscript/subscript formatting |
| `direction` | `ltr`, `rtl` | Text direction |
| `editor` | `removeformat`, `paste_text`, `fullscreen` | Editor utility actions |
| `advanced` | (custom items) | For custom toolbar extensions |
| `dropdowns` | (custom items) | Custom dropdown menus |
| `sections` | (custom items) | Custom toolbar sections |
| `extensions` | (custom items) | Custom toolbar extensions |

Each action supports these common options:
- `hideInline` (boolean) — hide from inline toolbar
- `label` (string) — custom button label
- `shortcut` (string) — keyboard shortcut

### Top-Level RTE Properties

| Property | Required | Description |
|----------|----------|-------------|
| `toolbar` | Yes | Toolbar category configuration (object, string, or boolean) |
| `plugins` | Yes | Space-separated list of enabled plugins |
| `icons` | Yes | Icon set name |
| `icons_url` | Yes | URL to icon set JS |
| `skin_url` | Yes | URL to editor skin |
| `inline` | No | Whether editor is inline |
| `menubar` | No | Show/hide menu bar |
| `statusbar` | No | Show/hide status bar |
| `toolbar_sticky` | No | Sticky toolbar on scroll |
| `toolbar_sticky_offset` | No | Offset for sticky toolbar |
| `toolbar_location` | No | Toolbar position |
| `content_css` | No | Custom CSS for editor content |
| `content_style` | No | Inline CSS for editor content |
| `min_width` | No | Minimum editor width |
| `unsupportedHtml` | No | Allow unsupported HTML |
| `selector` | No | CSS selector for the editor |
| `labels` | No | Custom labels (key-value object) |
| `advlist_bullet_styles` | No | Bullet list style options |
| `advlist_number_styles` | No | Numbered list style options |

### Example: Filter with RTE Configuration

```json
{
  "id": "section",
  "components": ["text", "image", "hero"],
  "rte": {
    "toolbar": {
      "format": ["bold", "italic", "underline"],
      "blocks": ["h2", "h3", "h4", "paragraph"],
      "list": ["bullet_list", "ordered_list"],
      "insert": ["link", "image"]
    },
    "plugins": "link lists image",
    "icons": "thin",
    "icons_url": "/icons/thin/icons.js",
    "skin_url": "/skins/oxide"
  }
}
```

> **Schema reference:** [filter-definition-rte.schema.json](https://universal-editor-service.adobe.io/schemas/filter-definition-rte.schema.json)
references/examples.md
# Real Examples from the Codebase

## Table of Contents
1. [Simple Block: Hero](#simple-block-hero)
2. [Simple Block: Embed](#simple-block-embed)
3. [Container Block: Cards](#container-block-cards)
4. [Block with Variants: Teaser](#block-with-variants-teaser)
5. [Key-Value Block: Product Details](#key-value-block-product-details)
6. [Block with Content Fragment: Article](#block-with-content-fragment-article)
7. [Section Configuration](#section-configuration)
8. [Block with Textarea: Metadata](#block-with-textarea-metadata)
9. [Block with Checkbox Group: Feature Toggles](#block-with-checkbox-group-feature-toggles)
10. [Filter with RTE Configuration](#filter-with-rte-configuration)

---

## Simple Block: Hero

A basic block with an image, alt text, and rich text content.

### _hero.json (distributed config)
```json
{
  "definitions": [
    {
      "title": "Hero",
      "id": "hero",
      "plugins": {
        "xwalk": {
          "page": {
            "resourceType": "core/franklin/components/block/v1/block",
            "template": {
              "name": "Hero",
              "model": "hero"
            }
          }
        }
      }
    }
  ],
  "models": [
    {
      "id": "hero",
      "fields": [
        {
          "component": "reference",
          "valueType": "string",
          "name": "image",
          "label": "Image",
          "multi": false
        },
        {
          "component": "text",
          "valueType": "string",
          "name": "imageAlt",
          "label": "Alt",
          "value": ""
        },
        {
          "component": "richtext",
          "name": "text",
          "value": "",
          "label": "Text",
          "valueType": "string"
        }
      ]
    }
  ],
  "filters": []
}
```

**Key patterns**:
- `image` + `imageAlt` pair: The pipeline collapses these into `<picture><img alt="...">`
- `richtext` for flexible content with formatting

---

## Simple Block: Embed

A block that embeds external content (YouTube, Vimeo, Twitter).

### component-definition.json entry
```json
{
  "title": "Embed",
  "id": "embed",
  "plugins": {
    "xwalk": {
      "page": {
        "resourceType": "core/franklin/components/block/v1/block",
        "template": {
          "name": "Embed",
          "model": "embed"
        }
      }
    }
  }
}
```

### component-models.json entry
```json
{
  "id": "embed",
  "fields": [
    {
      "component": "aem-content",
      "valueType": "string",
      "name": "url",
      "label": "URL"
    },
    {
      "component": "reference",
      "valueType": "string",
      "name": "image",
      "label": "Placeholder Image",
      "multi": false
    },
    {
      "component": "text",
      "valueType": "string",
      "name": "imageAlt",
      "label": "Placeholder Image Alt Text",
      "value": ""
    }
  ]
}
```

### component-filters.json entry
The embed block appears in the `section` filter:
```json
{
  "id": "section",
  "components": ["text", "image", "button", "title", "hero", "...", "embed"]
}
```

**Key patterns**:
- `aem-content` for URL field (allows AEM content paths and external URLs)
- Optional `image` + `imageAlt` for placeholder thumbnail

---

## Container Block: Cards

A block with repeatable child items.

### _cards.json
```json
{
  "definitions": [
    {
      "title": "Cards",
      "id": "cards",
      "plugins": {
        "xwalk": {
          "page": {
            "resourceType": "core/franklin/components/block/v1/block",
            "template": {
              "name": "Cards",
              "filter": "cards"
            }
          }
        }
      }
    },
    {
      "title": "Card",
      "id": "card",
      "plugins": {
        "xwalk": {
          "page": {
            "resourceType": "core/franklin/components/block/v1/block/item",
            "template": {
              "name": "Card",
              "model": "card"
            }
          }
        }
      }
    }
  ],
  "models": [
    {
      "id": "card",
      "fields": [
        {
          "component": "reference",
          "valueType": "string",
          "name": "image",
          "label": "Image",
          "multi": false
        },
        {
          "component": "richtext",
          "name": "text",
          "value": "",
          "label": "Text",
          "valueType": "string"
        }
      ]
    }
  ],
  "filters": [
    {
      "id": "cards",
      "components": ["card"]
    }
  ]
}
```

**Key patterns**:
- Container (Cards) has `filter` in template, NO `model`
- Item (Card) has `model` in template, uses `block/item` resourceType
- Filter restricts container to only accept card items
- Both container and item need separate definition entries

---

## Block with Variants: Teaser

A complex block with style variants via multiselect, multiple fields with defaults.

### Key component-definition.json entry
```json
{
  "title": "Teaser",
  "id": "teaser",
  "plugins": {
    "xwalk": {
      "page": {
        "resourceType": "core/franklin/components/block/v1/block",
        "template": {
          "name": "Teaser",
          "model": "teaser",
          "title": "Teaser Title",
          "titleType": "h3",
          "longDescr": "",
          "classes": ["light", "left"]
        }
      }
    }
  }
}
```

### Key model fields (abbreviated)
```json
{
  "id": "teaser",
  "fields": [
    {
      "component": "reference",
      "name": "fileReference",
      "label": "Image",
      "multi": false
    },
    {
      "component": "multiselect",
      "name": "classes",
      "label": "Style",
      "required": true,
      "maxSize": 3,
      "options": [
        {
          "name": "Theme",
          "children": [
            { "name": "Light", "value": "light" },
            { "name": "Dark", "value": "dark" }
          ]
        },
        {
          "name": "Alignment",
          "children": [
            { "name": "Left", "value": "left" },
            { "name": "Right", "value": "right" }
          ]
        }
      ]
    },
    {
      "component": "select",
      "name": "titleType",
      "value": "h3",
      "label": "Heading Type",
      "options": [
        { "name": "H1", "value": "h1" },
        { "name": "H2", "value": "h2" },
        { "name": "H3", "value": "h3" }
      ]
    }
  ]
}
```

**Key patterns**:
- `classes` field with grouped multiselect → CSS classes on the block div
- Template pre-populates default values (`classes: ["light", "left"]`)
- `titleType` suffix → heading level in rendered HTML

---

## Key-Value Block: Product Details

Renders as a 2-column key-value table rather than a grid.

### component-definition.json entry
```json
{
  "title": "Product Details",
  "id": "product-details",
  "plugins": {
    "xwalk": {
      "page": {
        "resourceType": "core/franklin/components/block/v1/block",
        "template": {
          "name": "Product Details",
          "model": "product-details",
          "key-value": true
        }
      }
    }
  }
}
```

**Key pattern**: `"key-value": true` in the template changes how the block renders in the pipeline (2-column format: property name | value).

---

## Block with Content Fragment: Article

Uses a Content Fragment reference.

### component-models.json entry
```json
{
  "id": "article",
  "fields": [
    {
      "component": "aem-content-fragment",
      "name": "articlepath",
      "value": "",
      "label": "Article Content Fragment path",
      "valueType": "string"
    },
    {
      "component": "text",
      "name": "variation",
      "value": "",
      "label": "Variation",
      "valueType": "string"
    }
  ]
}
```

**Key pattern**: `aem-content-fragment` component for selecting Content Fragments from the DAM.

---

## Section Configuration

Sections use the same model/filter system but with a different resource type.

### component-definition.json
```json
{
  "title": "Section",
  "id": "section",
  "plugins": {
    "xwalk": {
      "page": {
        "resourceType": "core/franklin/components/section/v1/section",
        "template": {
          "model": "section"
        }
      }
    }
  }
}
```

### component-models.json
```json
{
  "id": "section",
  "fields": [
    {
      "component": "text",
      "name": "name",
      "label": "Section Name",
      "description": "The label shown for this section in the Content Tree"
    },
    {
      "component": "multiselect",
      "name": "style",
      "label": "Style",
      "options": [
        { "name": "Highlight", "value": "highlight" },
        { "name": "Background Color", "value": "background" }
      ]
    }
  ]
}
```

### component-filters.json
```json
{
  "id": "section",
  "components": [
    "text", "image", "button", "title", "hero", "article", "cards",
    "columns", "teaser", "fragment", "offer", "storelocator", "blog-list",
    "product-recommendations", "product-teaser", "product-list-page",
    "product-details", "video", "form", "embed-adaptive-form",
    "reward", "reward-sticky", "quiz", "embed"
  ]
}
```

The `section` filter is the main gatekeeper: any block that should be available to authors **must** be listed here.

---

## Block with Textarea: Metadata

A block using `textarea` for multi-line description input.

### component-models.json entry
```json
{
  "id": "metadata",
  "fields": [
    {
      "component": "text",
      "name": "title",
      "label": "Page Title",
      "valueType": "string"
    },
    {
      "component": "textarea",
      "name": "description",
      "label": "Page Description",
      "valueType": "string"
    },
    {
      "component": "aem-tag",
      "name": "tags",
      "label": "Tags",
      "valueType": "string"
    }
  ]
}
```

**Key patterns**:
- `textarea` for multi-line plain text (no formatting needed)
- `aem-tag` for content categorization

---

## Block with Checkbox Group: Feature Toggles

A block using `checkbox-group` for multiple independent boolean options.

### component-models.json entry
```json
{
  "id": "banner",
  "fields": [
    {
      "component": "richtext",
      "name": "text",
      "label": "Banner Text",
      "valueType": "string"
    },
    {
      "component": "checkbox-group",
      "name": "features",
      "label": "Display Options",
      "valueType": "string[]",
      "options": [
        { "name": "Show Close Button", "value": "show-close" },
        { "name": "Show Icon", "value": "show-icon" },
        { "name": "Sticky Position", "value": "sticky" }
      ]
    }
  ]
}
```

**Key patterns**:
- `checkbox-group` with `valueType: "string[]"` for multiple independent toggles
- Each option is independently selectable (unlike `radio-group` which is mutually exclusive)

---

## Filter with RTE Configuration

A filter that customizes the richtext editor toolbar available to authors.

### component-filters.json entry
```json
{
  "id": "section",
  "components": ["text", "image", "button", "title", "hero", "cards", "embed"],
  "rte": {
    "toolbar": {
      "format": ["bold", "italic", "underline"],
      "blocks": ["h2", "h3", "h4", "paragraph"],
      "list": ["bullet_list", "ordered_list"],
      "insert": ["link"]
    },
    "plugins": "link lists",
    "icons": "thin",
    "icons_url": "/icons/thin/icons.js",
    "skin_url": "/skins/oxide"
  }
}
```

**Key patterns**:
- `rte` property on a filter entry configures the richtext editor toolbar
- `toolbar` groups actions into categories (format, blocks, list, insert)
- `plugins` is a space-separated list of enabled editor plugins
- `icons`, `icons_url`, `skin_url` are required for the RTE to render
references/field-types.md
# Component Model Field Types Reference

> **Canonical source:** These field types are defined by the Universal Editor JSON schemas:
> - [model-definition-fields.schema.json](https://universal-editor-service.adobe.io/schemas/model-definition-fields.schema.json)
> - [model-definition.schema.json](https://universal-editor-service.adobe.io/schemas/model-definition.schema.json)

## Field Structure

Every field in a component model follows this base structure:

```json
{
  "component": "<field-type>",
  "name": "<property-name>",
  "label": "<Display Label>",
  "valueType": "<data-type>",
  "value": "<default-value>",
  "description": "<helper-text>"
}
```

**Required properties for all fields:** `component`, `label`, `name`.

Some component types require additional properties — see the per-component sections and the [Required Properties](#required-properties-by-component) table.

---

## Component Types

### Text Input Fields

#### `text`
Single-line text input. Used for short strings like titles, labels, alt text.
- **Enforced valueType:** `"string"`
- **Validation:** `minLength`, `maxLength`, `regExp`, `customErrorMsg`

```json
{
  "component": "text",
  "name": "title",
  "label": "Title",
  "valueType": "string"
}
```

#### `textarea`
Multi-line text input without rich formatting. Use for descriptions, notes, or longer plain text content. Distinct from `richtext` which includes formatting controls.
- **Enforced valueType:** `"string"`

```json
{
  "component": "textarea",
  "name": "description",
  "label": "Description",
  "valueType": "string"
}
```

#### `richtext`
Rich text editor with formatting controls (bold, italic, lists, links). Toolbar can be customized via RTE filter configuration (see architecture.md).
- **Enforced valueType:** `"string"`

```json
{
  "component": "richtext",
  "name": "text",
  "value": "",
  "label": "Text",
  "valueType": "string"
}
```

### Media & Content Fields

#### `reference`
AEM asset picker. Opens DAM browser for selecting images, videos, documents. Unlike `aem-content`, this can only reference assets.
- **Enforced valueType:** `"string"`

```json
{
  "component": "reference",
  "name": "image",
  "label": "Image",
  "valueType": "string",
  "multi": false
}
```
- Set `multi: true` for multiple asset selection
- Typically paired with a `text` field for alt text (e.g., `imageAlt`)

#### `aem-content`
AEM content picker. Can select any AEM resource — pages, URLs, content paths. Unlike `reference` which is limited to assets.
- **Flexible valueType** (any from enum, defaults to `"string"`)
- **Validation:** `rootPath` — limits content picker to a specific directory

```json
{
  "component": "aem-content",
  "name": "link",
  "label": "Link",
  "valueType": "string"
}
```

#### `aem-content-fragment`
Content Fragment picker. Selects AEM Content Fragments — structured content reusable across channels.
- **Flexible valueType** (any from enum, defaults to `"string"`)

```json
{
  "component": "aem-content-fragment",
  "name": "articlepath",
  "value": "",
  "label": "Article Content Fragment path",
  "valueType": "string"
}
```

#### `aem-experience-fragment`
Experience Fragment picker. Selects AEM Experience Fragments — groups of one or more components including content and layout that can be referenced and reused across pages.
- **Flexible valueType** (any from enum, defaults to `"string"`)

```json
{
  "component": "aem-experience-fragment",
  "name": "fragment",
  "label": "Experience Fragment",
  "valueType": "string"
}
```

#### `aem-tag`
AEM tag picker for content categorization and organization.
- **Enforced valueType:** `"string"`

```json
{
  "component": "aem-tag",
  "name": "tags",
  "label": "Tags",
  "valueType": "string"
}
```

### Selection Fields

#### `select`
Single-choice dropdown. Requires `options` array.
- **Enforced valueType:** `"string"`
- **Required properties:** `options`

```json
{
  "component": "select",
  "name": "titleType",
  "label": "Title Type",
  "valueType": "string",
  "value": "h2",
  "options": [
    { "name": "H1", "value": "h1" },
    { "name": "H2", "value": "h2" },
    { "name": "H3", "value": "h3" }
  ]
}
```

#### `multiselect`
Multiple-choice selector. Supports grouped options via `children`. Requires `options` array.
- **Enforced valueType:** `"string"` (not `"string[]"` — this is enforced by the schema)
- **Required properties:** `options`

```json
{
  "component": "multiselect",
  "name": "style",
  "label": "Style",
  "valueType": "string",
  "options": [
    { "name": "Highlight", "value": "highlight" },
    { "name": "Dark", "value": "dark" }
  ]
}
```

**Grouped options** (with `children`):
```json
{
  "component": "multiselect",
  "name": "classes",
  "label": "Style",
  "valueType": "string",
  "maxSize": 3,
  "options": [
    {
      "name": "Theme",
      "children": [
        { "name": "Light", "value": "light" },
        { "name": "Dark", "value": "dark" }
      ]
    },
    {
      "name": "Alignment",
      "children": [
        { "name": "Left", "value": "left" },
        { "name": "Right", "value": "right" }
      ]
    }
  ]
}
```

#### `checkbox-group`
Multiple true/false checkbox items. Users can select multiple options simultaneously. Requires `options` array.
- **Enforced valueType:** `"string[]"`
- **Required properties:** `options`

```json
{
  "component": "checkbox-group",
  "name": "features",
  "label": "Features",
  "valueType": "string[]",
  "options": [
    { "name": "Show Title", "value": "show-title" },
    { "name": "Show Image", "value": "show-image" },
    { "name": "Show CTA", "value": "show-cta" }
  ]
}
```

#### `radio-group`
Radio button group for mutually exclusive choices. Requires `options` array.
- **Enforced valueType:** `"string"`
- **Required properties:** `options`

```json
{
  "component": "radio-group",
  "name": "orientation",
  "label": "Display Options",
  "valueType": "string",
  "value": "horizontal",
  "options": [
    { "name": "Horizontally", "value": "horizontal" },
    { "name": "Vertically", "value": "vertical" }
  ]
}
```

### Data Fields

#### `boolean`
Toggle for true/false values.
- **Enforced valueType:** `"boolean"`
- **Validation:** `customErrorMsg`

```json
{
  "component": "boolean",
  "name": "hideHeading",
  "label": "Hide Heading",
  "description": "Hide the heading of the block",
  "valueType": "boolean",
  "value": false
}
```

#### `number`
Numeric input with optional min/max constraints.
- **Enforced valueType:** `"number"`
- **Validation:** `numberMin`, `numberMax`, `customErrorMsg`

```json
{
  "component": "number",
  "name": "maxItems",
  "label": "Max Items",
  "valueType": "number",
  "description": "Maximum number of items to display"
}
```

#### `date-time`
Date/time picker.
- **Enforced valueType:** `"date"`

```json
{
  "component": "date-time",
  "name": "startDate",
  "label": "Start Date",
  "valueType": "date"
}
```

### Structural Fields

#### `container`
Groups nested fields together. Used for composite fields or repeated field groups.
- **Flexible valueType** (any from enum)

```json
{
  "component": "container",
  "name": "ctas",
  "label": "Call to Actions",
  "collapsible": false,
  "multi": true,
  "fields": [
    { "component": "richtext", "name": "text", "label": "Text", "valueType": "string" },
    { "component": "aem-content", "name": "link", "label": "Link" }
  ]
}
```
- `collapsible`: Whether the group can be collapsed in the UI
- `multi: true`: Makes the container repeatable (add/remove instances). Container nesting is not permitted for multi-fields.
- `fields`: Array of nested field definitions

#### `tab`
Creates a tab separator in the properties panel. Not a data field — purely UI organization. Everything after a `tab` is placed on that tab until a new `tab` is encountered.
- **Flexible valueType** (any from enum)

```json
{
  "component": "tab",
  "label": "Validation",
  "name": "validation"
}
```

---

## valueType Constraints

The `valueType` property controls data validation. Most components **enforce** a specific value (marked as `const` in the schema). Only a few allow any value from the enum.

| Component | Enforced valueType | Flexibility |
|---|---|---|
| `text` | `"string"` | Enforced |
| `textarea` | `"string"` | Enforced |
| `richtext` | `"string"` | Enforced |
| `reference` | `"string"` | Enforced |
| `select` | `"string"` | Enforced |
| `multiselect` | `"string"` | Enforced |
| `radio-group` | `"string"` | Enforced |
| `checkbox-group` | `"string[]"` | Enforced |
| `boolean` | `"boolean"` | Enforced |
| `number` | `"number"` | Enforced |
| `date-time` | `"date"` | Enforced |
| `aem-tag` | `"string"` | Enforced |
| `aem-content` | Any | Flexible |
| `aem-content-fragment` | Any | Flexible |
| `aem-experience-fragment` | Any | Flexible |
| `container` | Any | Flexible |
| `tab` | Any | Flexible |

**Valid valueType enum values:** `"string"`, `"string[]"`, `"number"`, `"date"`, `"boolean"`

---

## Required Properties by Component

All fields require `component`, `label`, and `name`. Some require additional properties:

| Component | Additional required properties |
|---|---|
| `select` | `options` |
| `multiselect` | `options` |
| `radio-group` | `options` |
| `checkbox-group` | `options` |
| All others | — |

---

## Field Properties

| Property | Type | Description |
|----------|------|-------------|
| `component` | string | **Required.** Field type |
| `name` | string | **Required.** Property name for data persistence. Underscore (`_`) not allowed with aem/xwalk plugins. Can be a nested path (e.g., `teaser/image/fileReference`). |
| `label` | string | **Required.** Display label in the property panel |
| `description` | string | Helper text shown below the field |
| `valueType` | string | Data type for the value (see constraints table above) |
| `value` | any | Default value. The UE will persist this if no value is set. |
| `multi` | boolean | Allow multiple values. Container nesting not permitted for multi-fields. |
| `required` | boolean | Whether the field must have a value before saving |
| `readOnly` | boolean | Field cannot be edited by authors |
| `hidden` | boolean | Field is hidden from the properties panel |
| `options` | array | Choices for select/multiselect/radio-group/checkbox-group. **Required** for those types. |
| `condition` | object | JSON Logic rule for showing/hiding the field dynamically |
| `validation` | object | Validation rules — component-specific (see below) |
| `maxSize` | number | Max selections for multiselect |
| `collapsible` | boolean | For containers: whether they can collapse |
| `fields` | array | For containers: nested field definitions |
| `raw` | any | Additional metadata passed to the component |

---

## Validation

Validation rules are component-specific. Each component type supports different constraints:

### TextValidation (for `text` only)
| Property | Type | Description |
|----------|------|-------------|
| `minLength` | number | Minimum characters allowed |
| `maxLength` | number | Maximum characters allowed |
| `regExp` | string | Regular expression the input must match |
| `customErrorMsg` | string | Custom error message on validation failure |

```json
{
  "component": "text",
  "name": "email",
  "label": "Email",
  "valueType": "string",
  "validation": {
    "regExp": "^[^@]+@[^@]+\\.[^@]+$",
    "customErrorMsg": "Please enter a valid email address"
  }
}
```

### NumberValidation (for `number` only)
| Property | Type | Description |
|----------|------|-------------|
| `numberMin` | number | Minimum value allowed |
| `numberMax` | number | Maximum value allowed |
| `customErrorMsg` | string | Custom error message on validation failure |

### BooleanValidation (for `boolean` only)
| Property | Type | Description |
|----------|------|-------------|
| `customErrorMsg` | string | Custom error message for invalid boolean values |

### AEMContentValidation (for `aem-content` only)
| Property | Type | Description |
|----------|------|-------------|
| `rootPath` | string | Limits content picker to this directory and subdirectories |

```json
{
  "component": "aem-content",
  "name": "link",
  "label": "Link",
  "valueType": "string",
  "validation": {
    "rootPath": "/content/mysite"
  }
}
```

---

## Conditional Fields

Use JSON Logic syntax to show/hide fields based on other field values:

```json
{
  "component": "text",
  "name": "customUrl",
  "label": "Custom URL",
  "valueType": "string",
  "condition": {
    "==": [
      { "var": "linkType" },
      "custom"
    ]
  }
}
```

Operators: `==`, `===`, `!=`, `!==`, and standard JSON Logic operators.

---

## Options Format

### Flat options
```json
"options": [
  { "name": "Display Name", "value": "stored-value" }
]
```

Each option requires `name` (display text) and `value` (persisted value).

### Grouped options (multiselect only)
```json
"options": [
  {
    "name": "Group Label",
    "children": [
      { "name": "Option A", "value": "a" },
      { "name": "Option B", "value": "b" }
    ]
  }
]
```

### With empty/default option
```json
"options": [
  { "name": "default", "value": "" },
  { "name": "primary", "value": "primary" },
  { "name": "secondary", "value": "secondary" }
]
```
    ue-component-model | Prompt Minder