Edge Delivery Services · 4 of 28
Exploring an AEM Edge Delivery Services project with Universal Editor, from repository structure to authored HTML and browser-side block enhancement.
In Chapter 3, I compared the different ways to use Edge Delivery Services and explained why I chose to explore AEM with Universal Editor (xwalk) for this learning series.
The next question is practical: once the project is running, where does the content live, where does the frontend code live, and how do those two parts meet in the browser?
I want to answer that by opening a typical xwalk project, identifying the files that matter, and following one simple Hero section from authoring to the final page. This chapter is about understanding the boundaries. We will build and configure blocks in detail in the next chapters.
Coming from traditional AEM development, I am used to looking for components under /apps, HTL files, Sling Models, and client libraries. In an EDS project with Universal Editor, I need a different mental map.
The authoring side is responsible for the content and the fields authors can edit. With xwalk, that content is managed in AEM, and Universal Editor provides the editing experience.
The frontend repository is responsible for how the delivered page is styled and enhanced. It contains block JavaScript, CSS, page-loading scripts, and authoring-related configuration files.
These are connected, but they are not the same thing. Changing a field definition is different from changing how a block behaves in the browser.
A project based on the EDS Universal Editor starter may contain files and folders similar to these. Exact names and optional files can vary with the starter version and project setup.
blocks/
scripts/
styles/
fonts/
icons/
models/
tools/
head.html
fstab.yaml
paths.json
component-definition.json
component-models.json
component-filters.json
xwalk.json
package.json
Rather than memorize the list, I group it by responsibility.
| Area | What I look for |
|---|---|
blocks/ | Block-specific JavaScript and CSS, such as blocks/hero/hero.js and hero.css |
scripts/ | Shared page loading and frontend utilities |
styles/, fonts/, icons/ | Site-wide styling and visual assets |
component-definition.json | Components available to authors in Universal Editor |
component-models.json | Editable fields and their authoring models |
component-filters.json | Rules controlling which components can be inserted in relevant containers |
models/ | Additional model-related source files in projects that use this organization |
fstab.yaml, paths.json, xwalk.json | Integration and path configuration; inspect the project's actual values before assuming their roles |
head.html | Shared markup or resources placed in the document head, depending on project setup |
package.json | Development scripts and dependencies |
tools/ | Supporting project tooling |
I would not try to explain every configuration key on day one. My first goal is to identify where I would make a change: authoring configuration, delivered content, block behavior, or shared styling.
Suppose I create a page with a Hero section containing a heading, a short description, and an image. This is a learning example, not a description of an internal project.
The conceptual flow looks like this:
The important observation is that the block starts with delivered markup. It does not necessarily fetch its heading, description, and image from a separate JSON API when the page loads.
Flow in one line: Universal Editor authoring → AEM-managed content → EDS-delivered HTML → browser-side block enhancement.
A visual diagram can be added later, but this flow is enough to follow the example.
The browser receives HTML that represents the authored page. For a block, that HTML has a structure the frontend code can inspect and enhance.
For example, a simplified Hero might arrive with content arranged in rows and cells. The exact markup depends on the authoring model and delivery transformation, so I should inspect the real output of my learning project rather than assume one universal DOM shape.
For example, the delivered block might contain markup resembling this simplified structure:
<div class="hero block">
<div>
<div><picture>...</picture></div>
<div>
<h1>Explore the collection</h1>
<p>A short introduction to the page.</p>
</div>
</div>
</div>
This is illustrative markup, not a guaranteed xwalk output. Actual rows, cells, wrappers, and elements depend on the component model and delivered content. Inspect the page in DevTools before writing selectors.
The Hero JavaScript can then find the relevant elements, add classes, reorganize markup if necessary, and create the final presentation. CSS controls the visual layout.
This is a useful change in mindset for an AEM developer:
Inspect first. Transform second. That is the habit I want to carry into the block chapters.
If the behavior belongs only to the Hero block, I would start in a block-specific file, typically something like:
blocks/
hero/
hero.js
hero.css
A block's decorate function receives the block element and can enhance the existing DOM. Here is a deliberately small illustration:
export default function decorate(block) {
block.classList.add('hero-ready');
}
This example only adds a class. It does not pretend to implement a complete Hero or prescribe the markup that every project must use. We will work through real block structure and decoration in Chapters 5 and 6.
Shared page orchestration belongs elsewhere. In many EDS starters, scripts/scripts.js and supporting utilities coordinate loading and decoration. I would not place every block's special behavior into a shared script simply because it runs on the page.
Now suppose the author needs to edit the Hero heading and image. That is not solved by adding a CSS class or changing hero.js.
The authoring experience needs to know what component can be inserted and which fields it exposes. In a typical xwalk project, files such as component-definition.json, component-models.json, and component-filters.json participate in that configuration.
A useful way to keep the responsibilities separate is:
| Question | First place I would investigate |
|---|---|
| Why can't I insert a component? | Component definitions and filters |
| Why isn't an expected field editable? | Component model and authoring configuration |
| Why is the authored text missing on the page? | Authored content and delivered HTML |
| Why does the block look wrong after loading? | Block DOM assumptions, JavaScript, and CSS |
These are starting points, not guarantees. A problem can cross more than one layer.
At a high level, the browser loads the delivered document and the EDS frontend scripts progressively enhance it. Blocks can be discovered and decorated as part of that process.
You may see loading organized into eager, lazy, and delayed phases in EDS projects. For now, I only want to establish why that matters: not every enhancement has to happen at the same moment, and page-level loading code should coordinate the work instead of every block inventing its own loading system.
We will explore the actual loading functions, their order, and their implications in the block development chapter. This chapter only needs the connection between delivered HTML and the code that enhances it.
When I open a local page through the development server, I find it useful to separate where the content comes from and where the frontend code comes from. In a typical local EDS workflow, the development tooling can use content from the configured source while serving the JavaScript and CSS from the local working copy. The exact behavior depends on the starter, its configuration, and the command used to run it.
For a page containing a Hero, I would trace the request like this: verify the page HTML, confirm that the Hero is recognized as a block, check whether its JavaScript and CSS are requested, and then inspect the DOM after decoration. This connects the repository files to something observable in the browser without assuming every setup uses the same URL or proxy arrangement.
If my Hero looks wrong, I would avoid editing JavaScript immediately. I would follow the content flow in order.
First, check authoring. Is the heading or image actually present in the learning page, and does Universal Editor expose the expected fields?
Next, inspect the delivered HTML. Is the content present before block decoration? If not, the issue may be upstream of the block JavaScript.
Then, check the block code. Is the expected block recognized? Does its JavaScript run? Does the code assume a DOM structure that is different from the HTML it received?
Finally, inspect styling. If the content and structure are correct but the layout is wrong, check the block CSS and shared styles.
This approach is more useful than treating every visible issue as a frontend bug. It also gives me a repeatable way to learn: identify the earliest stage where the result stops matching expectations.
I am intentionally leaving detailed block anatomy, decoration functions, authoring-model JSON, DOM transformation patterns, and complete component implementation for the next chapters.
Chapter 4 should give us a map. Once we have that map, individual files and functions are easier to understand because we know which responsibility they belong to.
blocks/ contains block-specific behavior and styling; shared scripts coordinate broader page behavior.In Chapter 5 — Understanding EDS Blocks, I will focus on the block itself: what makes a block different from a traditional AEM component, what HTML structure it starts with, and why its JavaScript works on existing markup.
That will prepare us for Chapter 6 — EDS Block Development Model, where we can go deeper into loading, decoration, and frontend behavior.
Get an email when I publish the next chapter. No spam — just new technical deep-dives.
Comments
Share feedback or questions about this blog post.
No comments yet. Be the first to share your thoughts.