Dynamic Publishing at the Edge with BYOM and App Builder
Explore dynamic publishing at the edge using Bring Your Own Markup and Adobe App Builder. Learn how this innovative approach delivers dynamic content directly at the edge without traditional CMS authoring. Discover how to fetch and transform external data into semantic HTML, ensuring fast and styled delivery. This session provides insights into real-world implementations, unlocking new publishing models for projects. Continue the conversation on Experience League and discover upcoming events.
Hi everyone and welcome to this session on dynamic publishing at the Edge. My name is Lars Aufvatt and I’m a senior technical architect at Adobe. Today I’ll walk you through how we are using Bringaround Knockup and Adobe App Builder to deliver dynamic content directly at the Edge without relying on traditional CMS authoring. This talk is based on real world implementation and is designed for developers who want to extend Edge delivery with custom logic, external content sources and full control over how Markup is delivered. We look at the architecture, the code and a working demo and by the end you’ll see how this approach can unlock new publishing models for your projects. Let’s get started. Let’s start with a real world challenge. Your product data, pricing and merchandising content live in external systems, not in AEM. But your pages still need to show that content, styled, dynamic and up to date. Traditional CMS publishing assumes that content is authored and published, but that model breaks when your content is external, dynamic and constantly changing. This session is about solving the problem. We’ll explore how Bringaround Knockup and Adobe App Builder work together to deliver dynamic content directly to the Edge using Edge delivery. Let’s dive in. This diagram illustrates how traditional CMS publishing and dynamic delivery can coexist within the same architecture. On the left we have the AEM authoring flow, ideal for editorial content, structured layouts and controlled publishing. On the right we introduce a dynamic extension. External content is fetched and transformed by App Builder, then delivered as semantic HTML using Bringaround Knockup. Both flows converge at the Edge delivery layer, where they share the same styling and runtime decoration blocks. This unified model allows developers to combine authored content with dynamic overlays, styled consistently and delivered at Edge delivery speed. It’s a flexible approach that supports both structured authoring and dynamic integration, enabling teams to meet a wider range of publishing needs. Here’s how the dynamic publishing flow works. We start with an external content source. This could be a commerce API, partner system or any structured data within an API. And whenever there’s an update that needs to be reflected on the web page, an App Builder webhook is triggered, the top left icon in the diagram. This calls the orchestration action and in that orchestration step we can perform some validation checks and if everything passes, we request the corresponding page to be previewed. At this point, Edge delivery’s overlay mechanism comes into play. We’ll look at that in more detail on the next slide. But for now, just note that this overlay routes the request to a second App Builder action, which fetches the external content and transforms it into semantic HTML. That HTML is then passed to Edge delivery, which ingests it alongside authored content using the same styling and runtime decoration blocks. This architecture gives us full control over how content is fetched, validated, formatted and delivered, without relying on CMS authoring or static builds. Now, let’s take a closer look at the overlay mechanism in Edge delivery. On the left-hand side of the slide, you’ll see an example of an overlay configuration in the content config of the Edge delivery configuration service for your page. You’ll notice it defines two sources. The default source, typically document authoring, and the overlay, which in our case points to the second App Builder action, our bring-your-own-markup endpoint. Let’s review how this fits into the request load when publishing.
When a preview request comes in, for example, “/products,” “/12345,” Edge delivery checks the overlay configuration and at present calls the overlay endpoint first. This means the request will always sit the App Builder action we’ve defined. That action is responsible for determining whether the path should be handled dynamically, and if not, it returns a 404.
In that case, the request falls back to the default source, which in turn either responds with a requested resource or with a 404. If the path is considered valid, the App Builder action fetches the external content and transforms it into semantic HTML.
This setup enables us to generate pages or fragments on demand without requiring them to exist in the CMS. Importantly, both authored and dynamic paths should use the same Edge delivery runtime, meaning they share styling, decoration, and performance characteristics.
Let’s go deeper into the App Builder part of the solution. To have something to show, we’ve set up a small demo wrapper with the App Builder code as well as the Edge delivery code. This demo wrapper integrates a random user API that responds with random users. And with this demo, we’ll see how we can trigger the creation of such a user and his corresponding page in Edge delivery.
Let’s have a look at the App Builder code structure. We have two focused web actions, the webhook and the data provider, each with a single JavaScript file that acts as the entry point and holds the logic. In the case of the data provider, we also have a template that contains the semantic HTML that we will fill with data. All wiring here lives in one config and that is the AppConfig.yaml. And here, let’s quickly have a look at how this is all set up. We can see we have the App Builder actions in the actions folder. We have the web sources in the web sources folder. And for the runtime actions, we have one package defined in our case, which is the bringYourOwnMarkup actions package that we can find here. And the two actions are defined here. The first action is a webhook with the entry point that we’ve explored previously. And we can see that we have some input parameters available for us. And specifically, we have the project coordinates, the authorization token, and an optional nationality parameter that I’ve added for demoing parameterized requests.
And the second action is the data provider. And here we can see one particular thing, which is that we include the template that we have here to be bundled with this action so that we can access it while the IORuntime action is invoked.
Next, we’ll step into the webhook’s orchestration. Therefore, we’ll close the config, open the webhook index.js, and I’ve read it. So this action orchestrates preview and publish and forwards a simple parameter nationality to the data provider. The entry point for this action is the main function, where we have set up some logging here. And if we go further down, we can see that we basically extract some parameters from the request, namely the project coordinates, as well as the token that is the Helix authorization token. Both are required to actually conduct the preview request. And the third one, which is optional, is the nationality parameter that we can use with the random user API to request a user from a certain country. And now we will do some validation as the project coordinates, as well as the authorization token are definitely required in our case. We just respond with a 400 in case they’re not present. And so that we can ensure that we have all that we need to fulfill the requirements for the request.
Next up, we need to know under which path we want to publish a certain resource or preview a certain resource. And as we are in a demo setup right now, I’ve just set up a small helper function that generates a path basically with the bring your own markup page in front of a timestamp. And if we go back, we can see that this page path is then used in the actual preview request. And let me briefly explain what’s happening here. So you can see there is a for loop that tries the preview request at a maximum of three times so that we can account for some unforeseen issues while previewing.
And once the preview request is successful, we basically break the loop and we are able to move on and publish the resource using a similar mechanism that we will see in a second. Just to note that the preview request needs to be successful before we can actually do the publish request. And if we go further, I’ll briefly mention how this request is being set up. So here you can see that this process event function event essentially generates the URL. So the admin helix page with the URI environment, which in our case is preview or live for real publication, the project coordinates which essentially target your organization, your page and the branch that your content lives on as well as the path which as we’ve seen before is our demo path where we have the bring your own markup page with the timestamp. We add the authorization token to the header and then as I mentioned before, just for demo we also might want to generate a demo user of a certain nationality. So we add this as a header as well. In this case, it’s the content source location header that we can use. And there’s another one that could be used. For example, if the random user API would require some form of authorization, we could also pass that along in this X-content source authorization header, for example.
Then the only thing left to do essentially is to actually trigger the request and handle the response accordingly. That’s the orchestration action. We validate, we preview with retries, publish on success and forward the nationality via single header. And next up, we’ll explore the data provider function that fetches, shapes and renders the page. So let’s close this one and we move over to data provider index.js. As this is the action that fetches the user, shapes the data and renders HTML by a handle path template. And the first thing that we notice here is the random API endpoint, which in our case will fetch the user from random user.me slash API. And as previously mentioned, the entry point for such an action is always the main function. We have some logging available to us. And the first thing that we actually need to do here is to ensure that we only handle requests for which the path that the resource is requested for starts with bring your own markup dash page, because that is how we’ve defined it in the previous webhook action.
If that is not the case, so this condition doesn’t hold true, we respond with a 404 because that tells actually that it needs to request the actual resource from the fallback or default source that is configured in the configuration.
Next, we extract the nationality parameter from the header. So if that has passed along, then we will add that to our URL. And if not, then it’s optional anyway. So we have our API URL that we will then call and to request the data for from and then if all goes well, we should have an API response that will allow us to extract the data and we can then go ahead, transform the data and basically put that into a normalized user data object, which allows us to then later on easily access this data in the handlebars bindings. And if we go along, then you will see that there is also a warning in case the API doesn’t respond how we would like it. As the API from time to time might be a bit unstable, I’ve added some fallback data here so that the process doesn’t break. But in the end, that doesn’t matter too much. I would say important is that we now have the normalized user data object ready and prepared for handlebars. So what we then need to do is to basically read the user profile template that contains the semantic HTML and pass it on to handlebars so that handlebars can create a template object out of it. And then once that is done, we can pass along our data that we have prepared for exactly this purpose and handlebars will then use the user full name, user phone, user picture mappings to fill out the template and render it as an HTML object. This object then in turn can be passed on to the response on the type text HTML, status telling edge delivery that this is basically content that can be injected and that then will end up at our browser and is ready to be decorated. And this describes the high level flow of this fetch transform render process in one action. And next we’ll tour the HTML template, its bindings and how it all comes together.
So then let’s open the template and what we can see here is a pretty standard and straightforward handlebars.js HTML template. We do have the bindings here which are pretty simple and straightforward as we’ve prepared all the data in the action itself. And we do expose some user data here for indexing by metadata. We do have some social tags defined and then the semantic HTML. Here I want to mention that we are using the boilerplate hero block. So this is the semantic HTML for the boilerplate hero block. We just added our H1 as well as our, in this case, demo image that is fixed for all the pages. And we also have some default content like the user management dashboard, H2 right here and the paragraph. And then we do have a custom block that has been defined to expose all the user data that we need in a way that we want to show it. And yeah, that’s about it. That’s the template, semantic markup, simple bindings and metadata ready for indexing and the next video will run the full flow to render this page end to end. With the App Builder actions deployed, we can now go ahead and run the flow end to end. First, I will show the actual configuration request one more time so that we can ensure that the bring your own markup App Builder endpoint is configured correctly, which as we can see here, it is, as we see the data provider action configured as the markup overlay endpoint. Now let’s trigger the webhook and here, please note that I’ve added a nationality parameter here. So the resulting demo user should be located in Germany if all goes well. So let’s try that out.
After a short while, we receive an answer and here we can see that the request was successful.
We’ve received a page path under which the page now lives. So if I open that here in the browser, then we can see that I’ve just for the demo, reuse the boilerplate code and here we have the user with this data located in Germany. So one more thing real quick. I wanted to show that this particular page that we have generated using the bring your own markup endpoint reuses the hero, uses the default content styling and also a custom block that I’ve generated so that we can have a view of the user’s data. And if we go back to the start page, you will also see that due to the exposed and indexed metadata that we have, we now automatically also have a user index, which as can be tried out with this generated block, can be used to search for users and for example, filters the user database based on what we have in the index. This use case is based on a real implementation and it shows how this architecture unlocked dynamic product delivery at scale. In many organizations, product data lists across pricing services, inventory APIs and platform. Instead of sinking this into the CMS, we configured an overlay that allows product page requests to be rendered directly by App Builder. App Builder fetches the latest product data, metadata, pricing, availability, promotions and transforms that into semantic HTML. Edge Delivery delivers this with the same set of blocks and styles as the authored part of the page. This enables instant delivery of dynamic product pages with no CMS bottlenecks while allowing react to market changes in real time.
For some cases, we’ve extended this approach and enriched the pages with real-time data injected via an edge work integration. For example, some product groups required the price to be updated quite frequently, while other data like ratings needed to be pulled in from a different source altogether. And another cool thing in this project, we’ve embedded this into legacy pages as fragments, enabling a smooth step-by-step migration to Edge Delivery. And we were able to showcase value fast this way. Let’s take a moment to recap what we’ve seen. We’ve started with a real-world challenge, delivering dynamic content that lists outside the CMS reliably styled and fast. We introduced a composable architecture using bring-your-own markup, App Builder and Edge Delivery, where external content is fetched, transformed and rendered alongside authored content. We’ve looked at how an overlay can be used to influence rendering sources, how App Builder actions handle logic and transformation, and how Edge Delivery ensures consistent styling and performance. The key takeaway should be this. Bring-your-own-markup lets you deliver content from any source with full control and speed, empowered by Edge Delivery’s native extensibility. This is a developer-first solution, and it’s flexible, scalable, and, as we’ve seen, ready to provide real-world publishing needs.
If you’d like to try this out yourself, we’ve published a working demo on GitHub. It includes the App Builder actions, bring-your-own-markup endpoint, and a sample Edge Delivery page. Everything you need to explore the architecture and build your own dynamic overlays. You can clone the repo, deploy your own App Builder actions, and see how bring-your-own-markup integrates with Edge Delivery in practice. The short link and QR code on the slide will take you directly to the repo. This is a great starting point if you want to extend Edge Delivery with custom logic, external content sources, or dynamic rendering.
Thanks again for joining this session. If you’d like to explore further, here are three resources to help you get started. The first link takes you to the official bring-your-own-markup docs. The second link shows how to configure the overlay. And finally, if you have questions, there is a thread in Experience League linked as well. We hope that this session gave you a clear view of what’s possible with bring-your-own-markup and App Builder, and we look forward to see what you are building.
This session — Dynamic Publishing at the Edge with BYOM and App Builder — features Lars Auffarth, Senior Technical Architect at Adobe, and was pre-recorded for Adobe Developers Live 2025. Lars demonstrates how Bring Your Own Markup and Adobe App Builder can deliver dynamic content directly at the edge without traditional CMS publishing. See how this architecture combines authored and dynamic content, fetches external data through App Builder actions, and renders semantic HTML at edge speed — unlocking new publishing models for real-world projects.
Special thanks to our sponsors Algolia and Ensemble for supporting Adobe Developers Live 2025.
Next Steps
- Continue the conversation on Experience League
- Discover upcoming events