Adobe Launch Extensions to the Rescue: Why I wrote my own Custom Code Action

on 26.04.2021 by Hannes Schmieding

Two years ago I wrote my own Adobe Launch Extension to address the issues I had with the Custom Code Rule Action (CCA) provided by Adobe’s Core Extension. Sparked by a recent discussion on twitter, I felt validated to finally make this post.

I am also releasing a new extension Friendly Helpers which includes a Synchronous Code Rule Action, my answer to the CCA. I am planning to add a few other nice additions over time. The code is open source. Refer to the end of this post for more details.


Let me start of by saying that I am a developer at heart. I agree custom code should be used sparingly in Launch. It makes the setup more complicated for those who are not as tech savvy. However, in many cases custom code is unavoidable if you want to build anything at scale, which is exactly what we do at FELD M. A well thought out data layer concept can help keep rules simple, but ultimately, ongoing additions will over time necessitate more and more solutions with custom code. The best example in my opinion are the technical implications of GDPR.

Before GDPR most pages fired all their analytics related stuff at page bottom. No more – everything is asynchronous now. The consent management tool of choice loads at some undefined point in time and Launch must wait and react to its response. Unless of course you bribed legal with actual cookies and they allowed tracking by default… only for it to be changed to an active opt-in two months later (yeah that happened). I cannot count the different forms of privacy implementations and interpretations I have seen at my clients over these last years. Suffice to say, they would not have been possible without frequent use of custom code.

A not-so warm welcome to Launch!

With DTM being officially sunset, plenty of migration projects were kicking off back in 2018 – and things were not going as smooth as hoped. Adobe provided plenty of information on how to migrate from one system to the other, see here, but the devil was in the details. The CCA and how Launch deals, or rather doesn’t deal, with the asynchronous execution it introduces was a frequent pain point.

Actually, the biggest problem I encountered wasn’t with the CCA at all, but rather the Adobe Analytics Extension. In DTM the population of the AppMeasurement object (aka “s” object) was performed synchronously with the triggering event. Not so in Launch. Under the hood the Adobe Analytics actions, like “Set Variables”, are wrapped in a Promise, meaning they will be executed at some later point in time after the triggering event, e.g. when the Analytics extension has loaded its dependencies, like the AppMeasurement. If your data layer changed in the meantime… you were out of luck and wrong data was sent to Adobe Analytics.

This problem led me to create my first Launch extension for an event-driven data layer (EDDL) with contextual data, akin to the Adobe Client Data Layer – just 2 years earlier and only 5kb in size….
It is in active use at “DER SPIEGEL”, “Schweizerische Bundesbahnen” and other clients in the DACH Region. The CCA, which I added more as a goody to the extension, has over time become a much-appreciated asset.

How the CCA works

Before I get into the technical reasons for why I made the Synchronous Code action, let’s have a brief look at how the CCA of the Adobe Core Extension works for JavaScript. Adobe’s Aaron Hardy has outlined the details in this community post.

In short: Custom JavaScript Code in Launch is handled in two ways.

Rules using page bottom or page top (library loaded) event
The code is included as a string in the Launch library file. On the client-side it is then wrapped in a script tag and added to the document using document.write.

Rules with any other event
The code is not bundled into the Launch library. Instead, it is a separate file which needs to be loaded. The code itself is just text, which is added to the document using postscribe.

Reason 1: Confusing

In DTM you could actively choose between “sequential” and “non-sequential” custom Javscript.

Launch also features two different implementations of custom code. However, it only provides one CCA, and – as outlined above – its behaviour is dependent on the event types of the rule. Which method is used is not readily apparent in the user interface. Either you know or you don’t. This is a big pain point, since there are significant technical implications. We should be able to actively choose which method to use.

Reason 2: Not immediate

Time critical events like click listeners and the unload/page hide event are common use cases where immediate code execution is required. Unfortunately, using the CCA in a rule with a simple click listener does not work. When a user clicks a link our code will have to be downloaded first, at which point the page context has likely been updated (Single Page Application => wrong URL/pagename) or is lost entirely as the browser navigates (=> no analytics hit is sent).

One solution is to plug your code into either conditions or events. These are always bundled directly into the Launch library. This solution solves the technical issues I have with the CCA, but it makes the rules less organized. I could look over this if it was just me working with the Launch setup.

Another potential solution using only the Core extension is to force the code bundling by adding a page bottom or page top event and cancelling it out in a condition. Urs Boller made an excellent blog post about this. Keep in mind, this approach bloats the rule a little. It also doesn’t solve the other technical issues.

Reason 3.1: Performance – Hosted Files and File Size

The developers at my clients were eyeing the performance of Launch critically, since, of course, we promised them a performance boost switching from DTM to Launch. The bundled file size, as well as the size of overall assets, were under scrutiny. And Adobe did well here. Launch is faster, and the bundling is smarter. E.g. It only loads what you actually need and use from your extensions.

However, beyond extensions, we lack fine control of this for custom code. We only have the Custom Code by Adobe Cores Extension and, as previously mentioned, the technical implications of GDPR and other asynchronous dependencies result in both page bottom and page top (library loaded) events being used less often. This leads to more separate code files, which are actually required on every page for analytics to work. They just don’t adhere to the standard triggers.

As such there are times where you absolutely want code to be bundled directly into the Launch library rather than having to load from a server first. It can be advantageous to load one larger file, rather than many smaller ones. It is one of the reasons JS code bundlers exists.

Furthermore, CCA code is not minified.

Lastly, simply including any CCA in your library, even if its just to add nothing more than console.log('yay it works') , adds around 6kb to your Launch Library file. Launch now has to add the logic, including postscribe, that facilitate the CCA behaviour.

Reason 3.2: Performance – document.write

A second performance implication is the continued use of document.write, which the CCA still makes use of to add the code to the document. I lost track of how many times developers have reached out to me to complain about it. This was a big issue at my clients. “You promised performance, and here you are using document.write”.

For the record, I have seen this behaviour by Launch less and less. However, document.write is included in the source code and thus developers occasionally flag it in reviews.

Reason 4: XHTML Error

Last year a client reached out to me. They had noticed missing data on certain pages. I checked. Everything was set up correctly. However, a key piece of custom code, using a JavaScript CCA bundled directly into library, was failing to load.
It turns out all affected pages were XHTML pages. The error was:

Failed to set the 'innerHTML' property on 'Element': The provided markup is invalid XML, and therefore cannot be inserted into an XML document.

Unfortunately, I am currently unable to reproduce this and the affected pages at my clients have been updated since. The “known errors” documentation at my client’s states that the solution at the time was to switch from the CCA to using the Synchronous Code action.

This may be related to how Launch stores Custom Code as a string, when bundled into the library. The string then needs to be converted to a function for execution at runtime. The insertion of the parsed code to the document was failing.

Solution: Synchronous Code Action

The reasons detailed above have led to me create a Synchronous Code action, where custom JavaScript code is directly bundled into the adobe Launch library as a function definition. It thus addresses all of the aforementioned issues.

The following code shows how the Synchronous Code action and the CCA (using a page top event) and are included in the launch-library.js.

    // [...]
    "actions": [
            "modulePath": "friendly-helpers-feldm/src/lib/actions/synchronousCode.js",
            "settings": {
                "callback": function (event, target, Promise) {
                    console.log('Friendly Helper', arguments, 'this:', this);
            "modulePath": "core/src/lib/actions/customCode.js",
            "settings": {
                "source": "console.log('Custom Code',arguments,'this:', this);",
                "language": "javascript"

Note that the CCA user code is a string, which needs to converted on the client side first.

The development of the extension itself, and the technical details are beyond the scope of this blog. To this point, I will simply drop the official documentation here as a good starting point.

Technical details for Extension Developers

The key piece which makes the Synchronous Code action possible is the transform feature in the extension manifest. See the documentation: extension manifest documentation > function transforms:

Using the function transform tells Platform Launch to wrap the user’s code in a executable function when it is emitted in the Platform Launch runtime library

The Custom Code for events and conditions in the Adobe’s Core Extension also use the function transform. The Synchronous Code action is simply the CCA using a function transform instead of the undocumented customCode transform it currently uses. We can see this in the extension manifest in the git repository of Adobe’s Core extension. At the time of writing only file, function and remove transforms are documented.

Another key piece of the puzzle – providing a GUI where users can enter their custom code – is fortunately something we don’t need write ourselves. Launch provides access to its built in code editor via the extension bridge API.

Introducing the “Friendly Helpers” Extension

I have developed a new Extension, “Friendly Helpers”, with the intention to add a useful set of features to Adobe Launch, which aren’t provided out of the box. Starting with the Synchronous Code action.

The code is open source and available here:

I intend to reach out to Adobe to get it published publicly, so that you can easily add it to your Launch property. In the meantime, it is unfortunately only available as a private extension. Meaning you have to install it for your organization. Instructions are provided in the readme in the repo.

The following further actions are planned to be added to the extension.

If you have any feature suggestions please reach out and I’ll see what I can do! Email:

The extension uses the Adobe Coral/Spectrum UI to give it the same look and feel as the Launch GUI. Have a look at the src/viewsfolder.

Leave a Reply

Your email address will not be published.