Skip to main content

Universal Editor

The Universal Editor (UE) is Adobe's framework-agnostic visual editor. For EDS, it replaces the classic AEM Page Editor and gives authors a true WYSIWYG experience on top of the same block-based pipeline that powers document-based authoring.

This chapter is about the block-side wiring - the three JSON config files plus the HTML attributes that tell UE what to make editable. Authoring concepts and when-to-pick-UE-vs-docs live in Authoring models.

The three JSON files​

Three files live at the root of an EDS project that supports UE:

FileWhat it controls
component-definition.jsonWhich blocks exist and the authoring fields each exposes
component-models.jsonThe schemas (data models) referenced by definitions
component-filters.jsonWhich blocks are allowed inside which container (sections, columns, etc.)

UE reads these on load and uses them to render the authoring sidebar, validate input, and limit insertion choices.

component-definition.json​

Lists every component (block) the editor should know about. Each entry has an id, a display title, and a plugins.xwalk.page.template block that says what kind of markup the block produces.

component-definition.json
{
"groups": [
{
"title": "Blocks",
"id": "blocks",
"components": [
{
"title": "Hero",
"id": "hero",
"plugins": {
"xwalk": {
"page": {
"resourceType": "core/franklin/components/block/v1/block",
"template": {
"name": "Hero",
"model": "hero",
"filter": "hero"
}
}
}
}
},
{
"title": "Cards",
"id": "cards",
"plugins": {
"xwalk": {
"page": {
"resourceType": "core/franklin/components/block/v1/block",
"template": {
"name": "Cards",
"model": "cards",
"filter": "cards"
}
}
}
}
}
]
}
]
}

component-models.json​

Defines the authoring fields for each component referenced above. This is what authors actually fill in.

component-models.json
{
"models": [
{
"id": "hero",
"fields": [
{
"component": "reference",
"valueType": "string",
"name": "image",
"label": "Background image",
"multi": false
},
{ "component": "text", "name": "imageAlt", "label": "Alt text" },
{ "component": "text", "name": "title", "label": "Heading" },
{ "component": "richtext", "name": "body", "label": "Body" },
{ "component": "aem-content", "name": "cta", "label": "CTA link" }
]
},
{
"id": "cards",
"fields": [
{ "component": "container", "name": "cards", "label": "Cards" }
]
}
]
}

Common field components: text, multiline, richtext, select, boolean, reference (for assets), aem-content (for content fragments / pages), container (for nested children).

component-filters.json​

Whitelists which components can be inserted inside which container.

component-filters.json
{
"filters": [
{
"id": "section",
"components": ["hero", "cards", "columns", "text", "image", "embed"]
},
{
"id": "cards",
"components": ["card-item"]
},
{
"id": "hero",
"components": []
}
]
}

The id matches the template.filter value in component-definition.json. An empty list means "leaf" - the block accepts no nested children.

Instrumentation: data-aue-* attributes​

When UE loads a page, it inspects the rendered HTML for data-aue-* attributes to know what's editable. Your block decoration code adds these attributes so the editor can hover-highlight, edit, and re-order elements in place.

AttributeWhat it marks
data-aue-resourceThe content resource (where the value persists)
data-aue-propWhich property of that resource is being shown
data-aue-typeField component (e.g. text, richtext, image, reference)
data-aue-labelLabel shown in the editor sidebar
data-aue-modelComponent model used for component insertion/editing
data-aue-filterFor container elements - which filter applies here
data-aue-behaviorcomponent for a whole block, container for a nested zone

Document-based authoring does not require these attributes. For UE, the source comes pre-instrumented from AEM or from the UE/XWalk authoring pipeline, and your block decoration must preserve the attributes when restructuring the DOM.

A pattern that survives both modes:

/blocks/hero/hero.js
export default function decorate(block) {
const image = block.querySelector('picture');
const heading = block.querySelector('h1, h2, h3');
const cta = block.querySelector('a');

// Preserve any UE instrumentation by moving nodes, not cloning
block.innerHTML = '';

const content = document.createElement('div');
content.classList.add('hero-content');
if (heading) content.append(heading);
if (cta) content.append(cta);

if (image) block.append(image);
block.append(content);
}

block.innerHTML = '' then block.append(...) is safe because the children retained their original data-aue-* attributes - you didn't clone, you re-parented.

Connecting to a content source​

The site's fstab.yaml points at the Edge Delivery servlet on AEM author (the layout used by the aem-boilerplate-xwalk template):

fstab.yaml
mountpoints:
/:
url: "https://author-p12345-e67890.adobeaemcloud.com/bin/franklin.delivery/{org}/{repo}/main"
type: "markup"
suffix: ".html"

Sites managed through the Configuration Service store the same information in the content.source section of the site configuration instead of fstab.yaml. paths.json then maps AEM paths such as /content/mysite/ to public URLs (see Development).

The rendered page also needs the Universal Editor connection metadata, usually in head.html:

head.html
<meta name="urn:adobe:aue:system:aemconnection" content="aem:https://author-p12345-e67890.adobeaemcloud.com">

UE is launched from the AEM Sites console (or via a deep link) at https://experience.adobe.com/#/@{tenant}/aem/editor/canvas/{path} and points at the same AEM author instance. The editor then loads the EDS preview of the page being edited inside an iframe and layers the authoring UI on top.

Tradeoffs vs document-based authoring​

AspectUniversal EditorDocument-based
Author UXVisual, in-contextWord / Docs
Field validationStrong (per-model)Weak (free text in tables)
TranslationAEM MSM, GLaaSManual or external service
Reusable fragmentsYes (Content Fragments)Limited (/fragments/ pattern)
Setup costHigh (AEM + UE config)Low (SharePoint / Drive folder)
Best forStructured content, governanceMarketing speed, low-skill authors

Common gotchas​

SymptomLikely causeFix
Block not visible in UEMissing entry in component-definition.jsonAdd it; restart UE
Block visible but not insertable into a sectionMissing in component-filters.jsonAdd to the relevant filter
Edits don't persistdata-aue-resource / data-aue-prop lost during decorationDon't clone nodes; re-parent them
Field shows wrong widgetcomponent mismatch in component-models.jsonCheck valid components: text, richtext, reference, etc.
Multilingual content not loadingWrong cf (content fragment) reference pathCheck aem-content field's reference resolution

See also​