Web Component

[AEM Headless as a Cloud Service]{class="badge informative"}

Example applications are a great way to explore the headless capabilities of Adobe Experience Manager (AEM). This Web Component application demonstrates how to query content using AEM’s GraphQL APIs using persisted queries and render a portion of UI, accomplished using pure JavaScript code.

Web Component with AEM Headless

View the source code on GitHub

Prerequisites prerequisites

The following tools should be installed locally:

AEM requirements

The Web Component works with the following AEM deployment options.

This example app relies on basic-tutorial-solution.content.zip to be installed and the required deployment configurations are in place.

The Web Component is designed to connect to an AEM Publish environment, however it can source content from AEM Author if authentication is provided in the Web Component’s person.js file.

How to use

  1. Clone the adobe/aem-guides-wknd-graphql repository:

    code language-shell
    $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
  2. Navigate to web-component sub-directory.

    code language-shell
    $ cd aem-guides-wknd-graphql/web-component
  3. Edit the .../src/person.js file to include the AEM connection details:

    In the aemHeadlessService object, update the aemHost to point to your AEM Publish service.

    code language-plain
    # AEM Server namespace
    # AEM GraphQL API and Persisted Query Details

    If connecting to an AEM Author service, in the aemCredentials object, provide local AEM user credentials.

    code language-plain
    # For Basic auth, use AEM ['user','pass'] pair (for example, when connecting to local AEM Author instance)
  4. Open a terminal and run the commands from aem-guides-wknd-graphql/web-component:

    code language-shell
    $ npm install
    $ npm start
  5. A new browser window opens the static HTML page that embeds the Web Component at http://localhost:8080.

  6. The Person Info Web Component is displayed on the web page.

The code

Below is a summary of how the Web Component is built, how it connects to AEM Headless to retrieve content using GraphQL persisted queries, and how that data is presented. The complete code can be found on GitHub.

Web Component HTML tag

A reusable Web Component (aka custom element) <person-info> is added to the ../src/assets/aem-headless.html HTML page. It supports host and query-param-value attributes to drive the behavior of the component. The host attribute’s value overrides aemHost value from aemHeadlessService object in person.js, and query-param-value is used to select the person to render.

        query-param-value="John Doe">

Web Component implementation

The person.js defines the Web Component functionality and below are key highlights from it.

PersonInfo element implementation

The <person-info> custom element’s class object defines the functionality by using the connectedCallback() life-cycle methods, attaching a shadow root, fetching GraphQL persisted query, and DOM manipulation to create the custom element’s internal shadow DOM structure.

// Create a Class for our Custom Element (person-info)
class PersonInfo extends HTMLElement {

    constructor() {
        // Create a shadow root
        const shadowRoot = this.attachShadow({ mode: "open" });


    // lifecycle callback :: When custom element is appended to document
    connectedCallback() {
        // Fetch GraphQL persisted query
        this.fetchPersonByNamePersistedQuery(headlessAPIURL, queryParamValue).then(
            ({ data, err }) => {
                if (err) {
                    console.log("Error while fetching data");
                } else if (data?.personList?.items.length === 1) {
                    // DOM manipulation
                    this.renderPersonInfoViaTemplate(data.personList.items[0], host);
                } else {
                    console.log(`Cannot find person with name: ${queryParamValue}`);


    //Fetch API makes HTTP GET to AEM GraphQL persisted query
    async fetchPersonByNamePersistedQuery(headlessAPIURL, queryParamValue) {
        const response = await fetch(

    // DOM manipulation to create the custom element's internal shadow DOM structure
    renderPersonInfoViaTemplate(person, host){
        const personTemplateElement = document.getElementById('person-template');
        const templateContent = personTemplateElement.content;
        const personImgElement = templateContent.querySelector('.person_image');
            host + (person.profilePicture._dynamicUrl || person.profilePicture._path));
        personImgElement.setAttribute('alt', person.fullName);

Register the <person-info> element

    // Define the person-info element
    customElements.define("person-info", PersonInfo);

Cross-origin resource sharing (CORS)

This Web Component relies on an AEM-based CORS configuration running on the target AEM environment and assumes that the host page runs on http://localhost:8080 in development mode and below is a sample CORS OSGi configuration for the local AEM Author service.

Please review deployment configurations for respective AEM service.