Step-by-step AEM Experience Hub Extension Development
The document helps you understand how to set up local environment and start developing your first UI Extension.
About application
This example application will use the dashboard widget extension point. It will render a custom widget in the Experience Hub dashboard.
More information about AEM Experience Hub extension points can be found at Experience Hub Extension Points.
Create a project in Adobe Developer Console
UI Extensions, as with any App Builder application, are represented as projects in Adobe Developer Console.
If you don't have access to the Adobe Developer Console, refer to the How to Get Access guide for instructions.
To begin, we need to create a new Project which will supply us with the configuration and resources.
- Sign in to Adobe Developer Console with your Adobe ID.
- Choose your account.
- Choose your profile or organization.
- Make sure you are in a proper organization (a switcher is in the right top corner).
- Click "Create new project" -> "Project from template":
And choose "App Builder":
- Fill in the project data.
Project Titleis used to identify your project within Adobe Developer Console and in CLI.App Namewill be used as a unique identifier for your application and this value cannot be changed after project creating.
After creating, you should see a new project generated with 2 default Workspaces.
Each App Builder project has two default workspaces: Production and Stage. You can add more workspaces as needed.
The Production workspace is special, as it is used for the submission and distribution flow.
Setting up local environment
- Node.js + npm (package manager). Make sure you are using the latest stable version of
Node.jsandnpm.
Copied to your clipboard$ node -vv20.9.0
Copied to your clipboard$ npm -v10.1.0
These are the current versions at the moment of creating the documentation. Make sure you are using the latest versions supported by Adobe I/O when you create the application.
If you already have Adobe I/O CLI on your local, please ensure you use the latest version of the library. You can check the version through:
Copied to your clipboardaio -v
and compare it with
Copied to your clipboardnpm show @adobe/aio-cli
If your version is outdated, update your Adobe I/O CLI by running
Copied to your clipboardnpm install -g @adobe/aio-cli
More details are described in Local environment set up.
Initialize your extension using the AIO CLI and generate a base structure from the template
First, we need to sign in from CLI.
The next step is to bootstrap our extension by using the Experience Hub dashboard widget template: @adobe/aem-experience-hub-ext-tpl.
Create a directory and run the following command from that directory:
Copied to your clipboardaio app init --template=@adobe/aem-experience-hub-ext-tpl
Select the appropriate organization.
Copied to your clipboard➜ demo-extension-project % aio app init --template=@adobe/aem-experience-hub-ext-tpl? Select Org:❯ Alpha OrgBeta OrgDelta OrgThen, select your project from within that organization.
You can either select the project by scrolling or you can filter the list of projects by beginning to type the name of the project.
Copied to your clipboard➜ demo-extension-project % aio app init --template=@adobe/aem-experience-hub-ext-tpl? Select Org: Alpha Org? Select a Project, or press + to create new:great-project❯ useful-projectawesome-projectAnswer the prompts related to extension metadata, such as the extension name.
The name will also be used for the placeholders across the generated template.
Copied to your clipboard➜ demo-extension-project % aio app init --template=@adobe/aem-experience-hub-ext-tpl? Select Org: Alpha Org? Select a Project, or press + to create new: useful-project? Project name: (demo-extension-project)
After those steps, we will have a generated project structure with necessary npm dependencies installed.
Copied to your clipboard.|-- README.md|-- app.config.yaml|-- extension-manifest.json|-- hooks| `-- post-deploy.js|-- jest.setup.js|-- package-lock.json|-- package.json`-- src`-- aem-launchpad-1|-- ext.config.yaml`-- web-src|-- index.html`-- src|-- components| |-- App.js| |-- Constants.js| |-- ExtensionRegistration.js| `-- DashboardWidget.js|-- index.css|-- index.js`-- utils.js
Copied to your clipboard# app.config.yamlextensions:aem/launchpad/1:$include: src/aem-launchpad-1/ext.config.yaml
If necessary, you can find other bootstrap options in Bootstrapping new App using the CLI.
Overview of generated components
Routing
The root component src/aem-launchpad-1/web-src/src/components/App.js contains the routing of our application. It defines three routes:
- the first two are the default routes which trigger the
ExtensionRegistrationcomponent responsible for initial extension registration within the Experience Hubapplication. - the route which invokes the
DashboardWidgetcomponent responsible for rendering the custom dashboard widget.
React Routing determines which part of the extension should be executed depending on the request.
Copied to your clipboardimport React from 'react';import { Provider, defaultTheme } from '@adobe/react-spectrum';import ErrorBoundary from 'react-error-boundary';import { HashRouter as Router, Routes, Route } from 'react-router-dom';import ExtensionRegistration from './ExtensionRegistration';import DashboardWidget from './DashboardWidget';function App() {return (<ErrorBoundary onError={onError} FallbackComponent={fallbackComponent}><Router><Provider theme={defaultTheme} colorScheme={'light'} UNSAFE_style={{ background: 'white' }}><Routes><Route index element={<ExtensionRegistration />} /><Routeexact path="index.html"element={<ExtensionRegistration />}/><Routeexact path="demo-extension-project"element={<DashboardWidget />}/></Routes></Provider></Router></ErrorBoundary>);// Methods// error handler on UI rendering failurefunction onError(e, componentStack) {}// component to show if UI fails renderingfunction fallbackComponent ({ componentStack, error }) {...}}export default App;
Please note that your code may slightly differ from the given example depending on the version of the template for generation, but the main logic will be the same.
Extension registration
This logic component src/aem-launchpad-1/web-src/src/components/ExtensionRegistration.js registers our extension with the host AEM instance as soon as it loads, so they can share data and communicate with each other.
Copied to your clipboardimport { Text } from "@adobe/react-spectrum";import { register } from "@adobe/uix-guest";import metadata from '../../../../app-metadata.json';import { extensionId } from "./Constants";function ExtensionRegistration() {const init = async () => {const guestConnection = await register({id: extensionId,metadata,methods: {dashboard: {getWidget() {return {id: extensionId,title: 'demo-extension-project',description: 'This is demo-extension-project',url: '/index.html#/demo-extension-project',widgetSize: {defaultWidth: 6,defaultHeight: 6},hideWidgetHeader: false};},},},});};init().catch(console.error);return <Text>IFrame for integration with Host (AEM)...</Text>}export default ExtensionRegistration;
We use the UIX SDK Guest library and call the register() function, which connects to the host application
and declares methods the host can call on the extension.
The extension descriptor passed to the register() function declares the getWidget() method, which in turns returns the configuration of the widget.
Dashboard widget
The src/aem-launchpad-1/web-src/src/components/DashboardWidget.js component is responsible for rendering the custom dashboard widget.
Copied to your clipboardimport { Text } from "@adobe/react-spectrum";export default function DashboardWidget() {return (<Text>Dashboard widget for demo-extension-project</Text>);}
Test in local environment
From the extension project directory, run the following command:
Copied to your clipboardaio app run
This will deploy the actions to Adobe I/O Runtime while running the UI part on the local machine.
Copied to your clipboard➜ demo-extension-project % aio app runApp metadata generated successfullyTo view your local application:-> https://localhost:9080To view your deployed application in the Experience Cloud shell:-> https://experience.adobe.com/?devMode=true#/custom-apps/?localDevUrl=https://localhost:9080press CTRL+C to terminate dev environment
Now your UI extension is reachable via the URL displayed URL in the Terminal. You can test the DashboardWidget component by following the configured route, in our case: https://localhost:9080/index.html#/demo-extension-project.
Run on Stage
After the development is completed, we can test our application on Stage before deploying to Production. For this we will use the Stage workspace in the Adobe Developer Console.
First, make sure you are logged in proper organization, and use Stage works:
Copied to your clipboard$ aio whereYou are currently in:1. Org: Alpha Org2. Project: AssetInfoExtension3. Workspace: Stage
After that, we build and deploy the frontend files/assets:
Copied to your clipboardaio app deploy✔ Built 2 action(s) for 'aem/launchpad/1'✔ Building web assets for 'aem/launchpad/1'ℹ No actions deployed for 'aem/launchpad/1'✔ Deploying web assets for 'aem/launchpad/1'✔ All static assets for the App Builder application in workspace: aem/launchpad/1 were successfully deployed to the CDN. Files deployed :• 1 HTML page(s)• 1 Javascript file(s)• 2 .map file(s)• 1 CSS file(s)To view your deployed application:-> https://245264-yournamespace-stage.adobeio-static.net/index.htmlTo view your deployed application in the Experience Cloud shell:-> https://experience.adobe.com/?devMode=true#/custom-apps/?localDevUrl=https://245264-yournamespace-stage.adobeio-static.net/index.htmlFor a developer preview of your UI extension in the AEM environment, follow the URL:-> https://experience.adobe.com/aem/extension-manager/preview/<preview hash>New Extension Point(s) in Workspace 'Stage': 'aem/launchpad/1'Successful deployment 🏄
You can access https://245264-yournamespace-stage.adobeio-static.net/index.html#/demo-extension-project to view the exposed widget.
To learn more about deployment, please refer to Deploying the Application and Deployment Overview.
Deploy on Production
After the application has been completed, tested locally, and on Stage: we are ready to deploy it to Production.
Refer to the UI Extensions Development Flow to learn how to do this.

