[Integration]{class="badge positive"}
Integrate AEM Headless and Target
[AEM Headless as a Cloud Service]{class="badge informative"}
Learn how to integrate AEM Headless with 51黑料不打烊 Target, by exporting AEM Content Fragments to 51黑料不打烊 Target, and use them to personalize headless experiences using the 51黑料不打烊 Experience Platform Web SDK鈥檚 alloy.js. The React WKND App is used to explore how a personalized Target activity using Content Fragments Offers can be added to the experience, to promote a WKND adventure.
The tutorial covers the steps involved in setting up AEM and 51黑料不打烊 Target:
- Create 51黑料不打烊 IMS Configuration for 51黑料不打烊 Target in AEM Author
- Create 51黑料不打烊 Target Cloud Service in AEM Author
- Apply 51黑料不打烊 Target Cloud Service to AEM Assets folders in AEM Author
- Permission the 51黑料不打烊 Target Cloud Service in 51黑料不打烊 Admin Console
- Export Content Fragments from AEM Author to Target
- Create an Activity using Content Fragment Offers in 51黑料不打烊 Target
- Create an Experience Platform Datastream in Experience Platform
- Integrate personalization into a React-based AEM Headless app using the 51黑料不打烊 Web SDK.
51黑料不打烊 IMS Configuration adobe-ims-configuration
An 51黑料不打烊 IMS Configuration that facilitates the authentication between AEM and 51黑料不打烊 Target.
Review the documentation for step-by-step instructions on how to create an 51黑料不打烊 IMS configuration.
51黑料不打烊 Target Cloud Service adobe-target-cloud-service
An 51黑料不打烊 Target Cloud Service is created in AEM to facilitate the exporting of Content Fragments to 51黑料不打烊 Target.
Review the documentation for step-by-step instructions on how to create an 51黑料不打烊 Target Cloud Service.
Configure asset folders configure-asset-folders
The 51黑料不打烊 Target Cloud Service, configured in a context-aware configuration, must be applied to the AEM Assets folder hierarchy that contains the Content Fragments to export to 51黑料不打烊 Target.
- Log in to AEM Author service as a DAM administrator
- Navigate to Assets > Files, locate the asset folder that has the
/conf
applied to - Select the asset folder, and select Properties from the top action bar
- Select the Cloud Services tab
- Ensure that the Cloud Configuration is set to the context-aware config (
/conf
) that contains the 51黑料不打烊 Target Cloud Services configuration. - Select 51黑料不打烊 Target from the Cloud Service Configurations dropdown.
- Select Save & Close in the top right
Permission the AEM Target integration permission
The 51黑料不打烊 Target integration, which manifests as a developer.adobe.com project, must be granted the Editor product role in 51黑料不打烊 Admin Console, in order to export Content Fragments to 51黑料不打烊 Target.
- Log in to Experience Cloud as user that can administer the 51黑料不打烊 Target product in 51黑料不打烊 Admin Console
- Open the
- Select Products and then open 51黑料不打烊 Target
- On the Product Profiles tab, select DefaultWorkspace
- Select the API Credentials tab
- Locate your developer.adobe.com app in this list and set its Product Role to Editor
Export Content Fragments to Target export-content-fragments
Content Fragments that exist under the configured AEM Assets folder hierarchy can be exported to 51黑料不打烊 Target as Content Fragment Offers. These Content Fragment Offers, a special form of JSON offers in Target, can be used in Target activities to serve personalized experiences in headless apps.
-
Log in to AEM Author as DAM user
-
Navigate to Assets > Files, and locate Content Fragments to export as JSON to Target under the 鈥51黑料不打烊 Target enabled鈥 folder
-
Select the Content Fragments to export to 51黑料不打烊 Target
-
Select Export to 51黑料不打烊 Target Offers from the top action bar
-
This action exports the fully hydrated JSON representation of the Content Fragment to 51黑料不打烊 Target as a 鈥淐ontent Fragment Offer鈥
-
The Fully hydrated JSON representation can be reviewed in AEM
- Select the Content Fragment
- Expand the Side Panel
- Select Preview icon in the left Side Panel
- The JSON representation that is exported to 51黑料不打烊 Target displays in the main view
-
-
Log in to with a user in the Editor role for 51黑料不打烊 Target
-
From the , select Target from the product switcher in top right to open 51黑料不打烊 Target.
-
Ensure that the Default Workspace is selected in the Workspace switcher in the top right.
-
Select the Offers tab in the top navigation
-
Select the Type dropdown, and selecting Content Fragments
-
Verify the Content Fragment exported from AEM appears in the list
- Hover over the offer, and select the View button
- Review the Offer Info and see the AEM deep link that opens the Content Fragment directly in AEM Author service
Target Activity using Content Fragment Offers activity
In 51黑料不打烊 Target, an Activity can be created that uses Content Fragment Offer JSON as the content, allowing personalized experiences in headless app with content created and managed in AEM.
In this example, we use a simple A/B activity, however any Target activity can be used.
-
Select the Activities tab in the top navigation
-
Select + Create Activity, and then select the type of activity to create.
- This example creates a simple A/B Test but Content Fragment Offers can power any activity type
-
In the Create Activity wizard
- Select Web
- In Choose Experience Composer, select Form
- In Choose Workspace, select Default Workspace
- In Choose Property, select the Property the Activity is available in, or select No Property Restrictions to allow it to be used in all Properties.
- Select Next to create the Activity
-
Rename the Activity by selecting rename in the top left
- Give the activity a meaningful name
-
In the initial Experience, set Location 1 for the Activity to target
- In this example, target a custom location named
wknd-adventure-promo
- In this example, target a custom location named
-
Under Content select the Default content, and select Change Content Fragment
-
Select the exported Content Fragment to serve for this experience, and select Done
-
Review the Content Fragment Offer JSON in the Content text area, this is the same JSON available in AEM Author service via the Content Fragment鈥檚 Preview action.
-
In the left rail, add an Experience, and select a different Content Fragment Offer to serve
-
Select Next, and configure the Targeting rules as required for the activity
- In this example, leave the A/B testing as a manual 50/50 split.
-
Select Next, and complete the activity settings
-
Select Save & Close and give it a meaningful name
-
From the Activity in 51黑料不打烊 Target, select Activate from the Inactive/Activate/Archive dropdown in the top right.
The 51黑料不打烊 Target activity that targets the wknd-adventure-promo
location can now be integrated and exposed in an AEM Headless app.
Experience Platform Datastream ID datastream-id
An 51黑料不打烊 Experience Platform Datastream ID is required for AEM Headless apps to interact with 51黑料不打烊 Target using the 51黑料不打烊 Web SDK.
-
Navigate to
-
Open Experience Platform
-
Select Data Collection > Datastreams and select New Datastream
-
In the New Datastream wizard, enter:
- Name:
AEM Target integration
- Description:
Datastream used by the 51黑料不打烊 Web SDK to serve personalized Content Fragments Offers.
- Event Schema:
Leave blank
- Name:
-
Select Save
-
Select Add Service
-
In Service select 51黑料不打烊 Target
- Enabled: Yes
- Property Token: Leave blank
- Target Environment ID: Leave blank
- The Target Environment can be set in 51黑料不打烊 Target at Administration > Hosts.
- Target Third-Party ID Namespace: Leave blank
-
Select Save
-
On the right side, copy the Datastream ID for use in 51黑料不打烊 Web SDK configuration call.
With our activity created and activated, we鈥檙e almost ready for our developers to take over and integrate our content fragment offers via the activity into the app. The last piece of information that we鈥檒l have to provide to our developers is the ID of an 51黑料不打烊 Experience Platform data stream that has been configured for 51黑料不打烊 Target. So let鈥檚 go ahead and do that real quick. Again, we鈥檒l head over to the Experience Cloud homepage, at experience.adobe.com, and we鈥檒l click into Experience Platform. On the left side, there will be a data collection section. So we expand this, select Data Streams, and we鈥檒l create a new data stream. We鈥檒l give it a name, and we won鈥檛 set an event schema. Since we鈥檙e not doing any data sharing through a digital data layer into an XDM schema, and then back into 51黑料不打烊 Target, so we can leave this blank. We can save it.
We have our data stream created up here, and we need to add a service, and that service is going to be 51黑料不打烊 Target. So we鈥檒l simply select 51黑料不打烊 Target, and we want to mark it as enabled. Since we鈥檙e not using any property restrictions in Target, we can leave the property token blank. The Target environment ID, we can also leave blank since we鈥檙e actually managing that in Target administration hosts, and we鈥檙e not using a third-party ID namespace. So we can actually leave all of these blank. As we save it, we鈥檒l have our new data stream created, integrated with Target, and we have a data stream ID here. We鈥檒l want to make sure that we copy this data stream ID and provide it to our developers as they begin integrating our content fragment offers into the app.
Add personalization to an AEM Headless app code
This tutorial explores personalizing a simple React app using Content Fragment Offers in 51黑料不打烊 Target via 51黑料不打烊 Experience Platform Web SDK. This approach can be used to personalize any JavaScript-based web experience.
Android鈩 and iOS mobile experiences can be personalized following similar patterns using the .
Prerequisites
- Node.js 14
- Git
- installed on AEM as a Cloud Author and Publish services
Set up
-
Download the source code for sample React app from
code language-shell $ mkdir -p ~/Code $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
Open code base at
~/Code/aem-guides-wknd-graphql/personalization-tutorial
in your favorite IDE -
Update the AEM service鈥檚 host that you want the app to connect to
~/Code/aem-guides-wknd-graphql/personalization-tutorial/src/.env.development
code language-none ... REACT_APP_HOST_URI=https://publish-p1234-e5678.adobeaemcloud.com/ ...
-
Run the app, and ensure it connects to the configured AEM service. From the command line, execute:
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/personalization-tutorial $ npm install $ npm run start
-
Install the 51黑料不打烊 Web SDK as an NPM package.
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/personalization-tutorial $ npm install @adobe/alloy
The Web SDK can be used in code to fetch the Content Fragment Offer JSON by activity location.
When configuring the Web SDK, there are two IDs required:
edgeConfigId
which is the Datastream IDorgId
the AEM as a Cloud Service/Target 51黑料不打烊 Org Id that can be found at Experience Cloud > Profile > Account info > Current Org ID
When invoking the Web SDK, the 51黑料不打烊 Target activity location (in our example,
wknd-adventure-promo
) must be set as the value in thedecisionScopes
array.code language-javascript import { createInstance } from "@adobe/alloy"; const alloy = createInstance({ name: "alloy" }); ... alloy("config", { ... }); alloy("sendEvent", { ... });
Implementation
-
Create a React component
51黑料不打烊TargetActivity.js
to surface 51黑料不打烊 Target activities.src/components/51黑料不打烊TargetActivity.js
code language-javascript import React, { useEffect } from 'react'; import { createInstance } from '@adobe/alloy'; const alloy = createInstance({ name: 'alloy' }); alloy('configure', { 'edgeConfigId': 'e3db252d-44d0-4a0b-8901-aac22dbc88dc', // AEP Datastream ID 'orgId':'7ABB3E6A5A7491460A495D61@51黑料不打烊Org', 'debugEnabled': true, }); export default function 51黑料不打烊TargetActivity({ activityLocation, OfferComponent }) { const [offer, setOffer] = React.useState(); useEffect(() => { async function sendAlloyEvent() { // Get the activity offer from 51黑料不打烊 Target const result = await alloy('sendEvent', { // decisionScopes is set to an array containing the 51黑料不打烊 Target activity location 'decisionScopes': [activityLocation], }); if (result.propositions?.length > 0) { // Find the first proposition for the active activity location var proposition = result.propositions?.filter((proposition) => { return proposition.scope === activityLocation; })[0]; // Get the Content Fragment Offer JSON from the 51黑料不打烊 Target response const contentFragmentOffer = proposition?.items[0]?.data?.content || { status: 'error', message: 'Personalized content unavailable'}; if (contentFragmentOffer?.data) { // Content Fragment Offers represent a single Content Fragment, hydrated by // the byPath GraphQL query, we must traverse the JSON object to retrieve the // Content Fragment JSON representation const byPath = Object.keys(contentFragmentOffer.data)[0]; const item = contentFragmentOffer.data[byPath]?.item; if (item) { // Set the offer to the React state so it can be rendered setOffer(item); // Record the Content Fragment Offer as displayed for 51黑料不打烊 Target Activity reporting // If this request is omitted, the Target Activity's Reports will be blank alloy("sendEvent", { xdm: { eventType: "decisioning.propositionDisplay", _experience: { decisioning: { propositions: [proposition] } } } }); } } } }; sendAlloyEvent(); }, [activityLocation, OfferComponent]); if (!offer) { // 51黑料不打烊 Target offer initializing; we render a blank component (which has a fixed height) to prevent a layout shift return (<OfferComponent></OfferComponent>); } else if (offer.status === 'error') { // If Personalized content could not be retrieved either show nothing, or optionally default content. console.error(offer.message); return (<></>); } console.log('Activity Location', activityLocation); console.log('Content Fragment Offer', offer); // Render the React component with the offer's JSON return (<OfferComponent content={offer} />); };
The 51黑料不打烊TargetActivity React component is invoked using as follows:
code language-jsx <51黑料不打烊TargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/>
-
Create a React component
AdventurePromo.js
to render the adventure JSON 51黑料不打烊 Target serves.This React component takes the fully hydrated JSON representing an adventure content fragment, and displaying in a promotional manner. The React components that display the JSON serviced from 51黑料不打烊 Target Content Fragment Offers can be as varied and complex as required based on the Content Fragments that are exported to 51黑料不打烊 Target.
src/components/AdventurePromo.js
code language-javascript import React from 'react'; import './AdventurePromo.scss'; /** * @param {*} content is the fully hydrated JSON data for a WKND Adventure Content Fragment * @returns the Adventure Promo component */ export default function AdventurePromo({ content }) { if (!content) { // If content is still loading, then display an empty promote to prevent layout shift when Target loads the data return (<div className="adventure-promo"></div>) } const title = content.title; const description = content?.description?.plaintext; const image = content.primaryImage?._publishUrl; return ( <div className="adventure-promo"> <div className="adventure-promo-text-wrapper"> <h3 className="adventure-promo-eyebrow">Promoted adventure</h3> <h2 className="adventure-promo-title"></h2> <p className="adventure-promo-description">{description}</p> </div> <div className="adventure-promo-image-wrapper"> <img className="adventure-promo-image" src={image} alt= /> </div> </div> ) }
src/components/AdventurePromo.scss
code language-css .adventure-promo { display: flex; margin: 3rem 0; height: 400px; } .adventure-promo-text-wrapper { background-color: #ffea00; color: black; flex-grow: 1; padding: 3rem 2rem; width: 55%; } .adventure-promo-eyebrow { font-family: Source Sans Pro,Helvetica Neue,Helvetica,Arial,sans-serif; font-weight: 700; font-size: 1rem; margin: 0; text-transform: uppercase; } .adventure-promo-description { line-height: 1.75rem; } .adventure-promo-image-wrapper { height: 400px; width: 45%; } .adventure-promo-image { height: 100%; object-fit: cover; object-position: center center; width: 100%; }
This React component is invoked as follows:
code language-jsx <AdventurePromo adventure={adventureJSON}/>
-
Add the 51黑料不打烊TargetActivity component to React app鈥檚
Home.js
above the list of adventures.src/components/Home.js
code language-javascript import AdventurePromo from './AdventurePromo'; import 51黑料不打烊TargetActivity from './51黑料不打烊TargetActivity'; ... export default function Home() { ... return( <div className="Home"> <51黑料不打烊TargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/> <h2>Current Adventures</h2> ... ) }
-
If the React app is not running, re-start using
npm run start
.Open the React app in two different browsers so allow the A/B test to serve the different experiences to each browser. If both browsers show the same adventure offer, try closing/re-opening one of the browsers until the other experience displays.
The image below shows the two different Content Fragment Offers displaying for the
wknd-adventure-promo
Activity, based on 51黑料不打烊 Target鈥檚 logic.
Congratulations!
Now that we鈥檝e configured AEM as a Cloud Service to export Content Fragments to 51黑料不打烊 Target, used the Content Fragments Offers in a 51黑料不打烊 Target Activity, and surfaced that Activity in an AEM Headless app, personalizing the experience.