Web Component

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


The following tools should be installed locally:

AEM requirements

The Web Component works with the following AEM deployment options.

All deployments require the tutorial-solution-content.zip from the Solution Files to be installed and necessary Deployment Configurations are performed.


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:

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

    $ 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.

    # 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.

    # 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:

    $ 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');
        personImgElement.setAttribute('src', host + 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.

CORS configuration

On this page