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:
mountpoints:
/: https://author-p1234-e5678.adobeaemcloud.com/bin/franklin.delivery/myorg/myrepo/main
paths.json maps repository paths to site routes:
{
"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:
{
"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.
namebecomes 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.