# Edge Delivery blocks authored in the Universal Editor (xwalk)

Verified 2026-09 against
<https://experienceleague.adobe.com/en/docs/experience-manager-learn/sites/edge-delivery-services/developing/universal-editor/1-new-code-project>.

A UE-authored Edge Delivery project starts from the `adobe-rnd/aem-boilerplate-xwalk` template
rather than the document-authored boilerplate. Content lives in AEM, not in a spreadsheet or
document source.

## Project wiring

`fstab.yaml` points at the AEM author environment:

```yaml
mountpoints:
  /: https://author-p1234-e5678.adobeaemcloud.com/bin/franklin.delivery/myorg/myrepo/main
```

`paths.json` maps repository paths to site routes:

```json
{
  "mappings": [
    "/content/myproject/:/"
  ],
  "includes": [
    "/content/myproject/"
  ]
}
```

If a page renders but the editor cannot resolve its resource, `paths.json` is the first thing to
check — the mapping is what turns a site route back into a repository path.

## Per-block JSON

Each block owns three definitions, conventionally in `blocks/<block>/_<block>.json`. A teaser block:

```json
{
  "definitions": [
    {
      "title": "Teaser",
      "id": "teaser",
      "plugins": {
        "xwalk": {
          "page": {
            "resourceType": "core/franklin/components/block/v1/block",
            "template": {
              "name": "Teaser",
              "model": "teaser"
            }
          }
        }
      }
    }
  ],
  "models": [
    {
      "id": "teaser",
      "fields": [
        { "component": "text",      "name": "title",    "label": "Title",    "valueType": "string" },
        { "component": "richtext",  "name": "body",     "label": "Body",     "valueType": "string" },
        { "component": "reference", "name": "image",    "label": "Image",    "valueType": "string" },
        { "component": "text",      "name": "ctaLabel", "label": "CTA label","valueType": "string" },
        { "component": "aem-content","name": "ctaLink", "label": "CTA link", "valueType": "string" }
      ]
    }
  ],
  "filters": []
}
```

- **definitions** — what appears in the editor's component list, and which model a new instance uses.
- **models** — the fields an author edits. `name` becomes the property name, and therefore the value
  your block's JavaScript reads; renaming it orphans existing content.
- **filters** — for a block that contains other blocks, the ids that may be placed inside it. A
  container block with an empty filter list accepts nothing.

## Container blocks

A block whose children authors manage needs a filter entry naming the allowed child ids, and the
rendered markup needs `data-aue-type="container"` on the element holding the children. The two must
agree: the filter controls what the editor offers, the attribute controls where it is inserted.

## Keeping the model and the code in step

The block's `decorate()` reads the DOM the model produced. When you add a field, add it to the model
and to the markup contract at the same time, and check an existing authored instance still decorates
— a field inserted in the middle shifts the row order that older blocks rely on.
