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

Tech Insights

7 min read

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:

.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

Migrating to AEM Cloud: The Unexpected Upside for Front-End Delivery

7 min read

April 24, 2026

Perfection Doesn’t Ship: Lessons from Carlos Macedo’s Engineering Journey

6 min read

April 21, 2026

Valentina Panova on career paths, confidence, and work-life balance

6 min

April 13, 2026

Women@Exadel

The Overlooked Tool That Streamlines AEM as a Cloud Service Migrations

3 min read

April 2, 2026

AEM

Digital Experience

Adobe Cloud Migration

Rethinking Configuration Management in AEM Cloud

3 min read

April 2, 2026

AEM

Digital Experience

Adobe Cloud Migration

Be bold and keep moving - Talk with Karen Hutchison, VP and Global Head of Talent

6 min read

March 29, 2026

#Exadel People

#Women at Exadel

Two people sitting at a table with a laptop.

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

Get In Touch