AEM Experience Fragments: Consuming Outside AEMAEM Experience Fragments: Consuming Outside AEM

Tech Insights

7 min read

Three dark pyramids with neon pink and blue light outlining their edges and casting reflections.

Table of Contents

Tags

#AEM

#Digital marketing technology

#Experience fragments

Share

You can push an Experience Fragment (XF) to an endpoint by using, for example, the 3rd party’s API (e.g. Facebook / Pinterest). A 3rd party can also pull an XF from AEM. Every XF has a unique URL that can be embedded/used. Embedding an HTML XF can be achieved by using an <iframe> or with web components.

Currently, AEM supports HTML ootb (for Adobe Target HTML and JSON offers are supported) but you can implement any format (e.g. image, json, etc.). In addition, you can use the “plain” selector to get just HTML. You can configure the “Experience Fragments HTML rendering output processor” to render specific tags, attributes, and classes:

  1. Go to http://localhost:4502/system/console/configMgr
  2. Find “Experience Fragments HTML rendering output processor”

There’s another way to consume an XF on an external page. We can implement a servlet which grabs all the necessary libs (js, css, fonts, etc.) as well as as the XF content and puts them into your page.

Code example:

A simple servlet which scavenges XF content, the needed js, css and wraps them into a json object.

@Component(
       immediate = true,
       service = Servlet.class,
       property = {
               "service.description=Simple Servlet",
               "service.vendor=Site.com",
               SLING_SERVLET_RESOURCE_TYPES + "=site-com/page/xf",
               SLING_SERVLET_EXTENSIONS + "=js"
       }
)
@Designate(ocd = XFJSServlet.XFJSServletConfig.class)
public class XFJSServlet extends SlingSafeMethodsServlet {
 
   private static final String JQUERY_LIB = "/etc.clientlibs/clientlibs/granite/jquery.js";
 
   @ObjectClassDefinition(name = "XF JS Servlet")
   public @interface XFtJSServletConfig {
   }
 
   @Reference
   private RequestResponseFactory requestResponseFactory;
 
   @Reference
   private SlingRequestProcessor requestProcessor;
 
   @Reference
   private HtmlLibraryManager htmlLibraryManager;
 
   @Reference
   private Externalizer externalizer;
 
   @Reference
   private SlingSettingsService settingsService;
 
   private boolean isPublish = false;
 
   @Activate
   protected void activate(ExperienceFragmentJSServletConfig config) {
       if(settingsService.getRunModes().contains(Externalizer.PUBLISH)) {
           this.isPublish = true;
       }
   }
 
   @Override
   protected void doGet(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) throws ServletException, IOException {
       PrintWriter out = response.getWriter();
 
       response.setContentType("application/javascript");
       JsonObject xfJson = new JsonObject();
 
       xfJson.addProperty("reqJquery", getLink(request, JQUERY_LIB));
 
       // get page clientlibs
       xfJson.add("cssLibs", getMainLibraries(request, LibraryType.CSS, "pagelibs", "pagelibs-critical"));
       xfJson.add("jsLibs", getMainLibraries(request, LibraryType.JS, "pagelibs"));
 
       getPageClientLibsContent(request);
 
       // get html
       String contentHtml = getPageContentHtml(request);
       contentHtml = contentHtml.replace(""/content", String.format(""%1$s", getLink(request, "/content")));
       String formattedString = new String(Base64.getEncoder().encode(contentHtml.getBytes()));
       xfJson.addProperty("content", formattedString);
 
       out.print("var xfDeliveryObj = " + xfJson + ";");
       out.print(getXfDeliveryJs());
 
   }
 
   private JsonElement getMainLibraries(SlingHttpServletRequest request, LibraryType libraryType, String ... categories) {
       Collection<clientlibrary> libraries = htmlLibraryManager.getLibraries(categories, libraryType, true, true);
       JsonArray cssLibs = new JsonArray();
       for (ClientLibrary library : libraries) {
           cssLibs.add(new JsonPrimitive(getLink(request, library.getPath() + libraryType.extension)));
       }
 
       return cssLibs;
   }
 
   private void getPageClientLibsContent(SlingHttpServletRequest request) {
 
       Page page = request.getResource().adaptTo(Page.class);
       Designer designer = request.getResourceResolver().adaptTo(Designer.class);
       Design pageDesign = null;
 
       // traverse up to find design path
       while (page != null && (pageDesign = designer.getDesign(page)) == null) {
           page = page.getParent();
       }
 
       // default if not design path is set
       if (pageDesign == null) {
           pageDesign = designer.getDesign(Designer.DEFAULT_DESIGN_PATH);
       }
   }
 
   private String getPageContentHtml(SlingHttpServletRequest request) throws ServletException, IOException {
       Resource xfResource = request.getResource();
 
       String resourcePath = xfResource.getPath() + ".min.html";
       HttpServletRequest req = requestResponseFactory.createRequest("GET", resourcePath);
 
       WCMMode.DISABLED.toRequest(req);
 
       ByteArrayOutputStream reqOut = new ByteArrayOutputStream();
       HttpServletResponse resp = requestResponseFactory.createResponse(reqOut);
 
       requestProcessor.processRequest(req, resp, request.getResourceResolver());
 
       return reqOut.toString();
   }
 
   private String getXfDeliveryJs() {
       Collection<clientlibrary> libraries = htmlLibraryManager.getLibraries(new String[]{"site.xf-js"}, LibraryType.JS, true, true);
       StringWriter jsContentString = new StringWriter();
       for (ClientLibrary library : libraries) {
           HtmlLibrary htmlLib = htmlLibraryManager.getLibrary(LibraryType.JS, library.getPath());
           try {
               IOUtils.copy(htmlLib.getInputStream(), jsContentString, "UTF-8");
           } catch (IOException e) {
           }
       }
 
       return jsContentString.toString();
   }
 
   private String getLink(SlingHttpServletRequest request, String relativePath) {
      return isPublish() ? externalizer.publishLink(request.getResourceResolver(), relativePath) :
              externalizer.authorLink(request.getResourceResolver(), relativePath);
   }
 
   public boolean isPublish() {
       return isPublish;
   }
}
</clientlibrary></clientlibrary>

A custom js lib which consumes json provided by the servlet and renders it:

File explorer showing xf folder with .content.xml, index.js, and js.txt files.

.content.xml:

<!--?xml version="1.0" encoding="UTF-8"?-->
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primarytype="cq:ClientLibraryFolder" categories="[site.xf-js]"></jcr:root>
 
Index.js:
(function(){
   console.log(xfDeliveryObj);
   var jqueryLib = xfDeliveryObj.reqJquery;
 
   var current = document.currentScript.parentNode;
 
   if (typeof jQuery === 'undefined') {
       current.appendChild(createScript(jqueryLib));
   }
 
   xfDeliveryObj.cssLibs.forEach(function(lib){
       current.appendChild(createStyle(lib));
   });
 
   xfDeliveryObj.jsLibs.forEach(function(lib){
       current.appendChild(createScript(lib));
   });
 
   current.insertAdjacentHTML("beforeEnd", b64DecodeUnicode(xfDeliveryObj.content));
 
   function createScript(link) {
       var el = document.createElement("script");
       el.src = link;
       return el;
   }
 
   function createStyle(link) {
       var el = document.createElement("link");
       el.rel = 'stylesheet';
       el.href = link;
       return el;
   }
   function b64DecodeUnicode(str) {
       return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
           return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
       }).join(''))
   }
})();

After that we can easily put an XF in an external page:

<title>xf_test</title>
 
 
<div>
   <div>External Content</div>
       <script src="https://www.site.com/content/experience-fragments/site-com/xf/xf.js"></script>
</div>

Author: Iryna Ason

Resource Hub

Our Latest Stories & Industry Insights

View Resource Hub

Whitepaper: Token Cost Optimization for Enterprise AI. A Practical Framework to Reduce LLM Costs by 50–80%

28 min read

June 9, 2026

Tokenmaxxing Is Not an AI Strategy — It’s a Liability

12 min read

June 8, 2026

LLM Cost Optimization: A Practical Framework for Enterprise AI Teams

16 min read

June 5, 2026

Test-Driven Development in the Age of AI Coding: Why TDD Has Become the Essential Skill

10 min read

June 5, 2026

Background Agents Are the Real Fix for Engineering Bottlenecks

14 min read

June 4, 2026

“Be curious, not judgmental”: A Conversation with Chris Donato, CRO of Exadel

9 min read

June 2, 2026
Two people sitting at a table with a laptop.

Let’s make your next project faster, safer, smarter.

Get In Touch