Composite Components in SPAs composite-components-in-spas

Composite components use the modular nature of AEM components by combining multiple base components into a single component. A common composite component use case is the card component, made of a combination of the image and text components.

When composite components are properly implemented within the AEM Single Page Application (SPA) Editor framework, the content authors can drag and drop such components as they would any other component, but still be able to individually edit each component making up the composite component.

This article demonstrates how you can add a composite component to your single page application to work seamlessly with the AEM SPA Editor.

Use Case use-case

This article will use the typical card component as its example use case. Cards are a common UI element for many digital experiences and are typically made up of an image and associated text or caption. An author wants to be able to drag-and-drop the whole card, but be able to individually edit the card’s image and customize the associated text.

Prerequisites prerequisites

The following models for supporting the composite component use cases require the following prerequisites.

Adding Composite Components to a SPA adding-composite-components

There are three different models for implementing your composite component depending on your SPA implementation within AEM.

The following sections give examples of implementing each case using the card component as an example.

The component does not exist in your AEM project. component-does-not-exist

Start by creating the components that will make up the composite component, that is, components for the image and its text.

  1. Create the text component in your AEM project.

  2. Add the corresponding resourceType from the project in the component’s editConfig node.

    code language-text
     resourceType: 'wknd-spa/components/text'
  3. Use the withMappable helper to enable editing for the component.

    code language-text
    export const AEMText = withMappable(Text, TextEditConfig);

The text component is similar to the following.

import React from 'react';
import { withMappable } from '@adobe/aem-react-editable-components';

export const TextEditConfig = {
  emptyLabel: 'Text',
  isEmpty: function(props) {
    return !props || !props.text || props.text.trim().length < 1;
  resourceType: 'wknd-spa/components/text'

export const Text = ({ cqPath, richText, text }) => {
  const richTextContent = () => (
    <div className="aem_text"
      id={cqPath.substr(cqPath.lastIndexOf('/') + 1)}
      dangerouslySetInnerHTML={{__html: text}} />
  return richText ? richTextContent() : (
     <div className="aem_text">{text}</div>

export const AEMText = withMappable(Text, TextEditConfig);

If you create an image component in a similar way, you can then combine it with the AEMText component into a new card component, using the image and text components as children.

import React from 'react';
import { AEMText } from './AEMText';
import { AEMImage } from './AEMImage';

export const AEMCard = ({ pagePath, itemPath}) => (
       itemPath={`text`} />
       itemPath={`image`} />

This resulting composite component can now be placed anywhere in the app and the will add placeholders for a text and an image component in the SPA Editor. In the sample below, the card component is added to the home component below the title.

function Home() {
  return (
    <div className="Home">
      <h2>Current Adventures</h2>
        pagePath='/content/wknd-spa/home' />

This will display an empty placeholder for a text and an image in the editor. When entering values for these using the editor, they are stored at the specified page path that is, /content/wknd-spa/home at the root level with the names specified in itemPath.

Composite card component in the editor

The component exists in your AEM project but its required content does not. content-does-not-exist

In this case, the card component is already created in your AEM project containing title and image nodes. The child nodes (text and image) have the corresponding resource types.

Node structure of the card component

You can then add it to your SPA and retrieve its content.

  1. Create a corresponding component in the SPA for this. Ensure that the child components are mapped to their corresponding AEM resource types within the SPA project. In this example we use the same AEMText and AEMImage components as detailed in the previous case.

    code language-javascript
    import React from 'react';
    import { Container, withMappable, MapTo } from '@adobe/aem-react-editable-components';
    import { Text, TextEditConfig } from './AEMText';
    import Image, { ImageEditConfig } from './AEMImage';
    export const AEMCard = withMappable(Container, {
      resourceType: 'wknd-spa/components/imagecard'
    MapTo('wknd-spa/components/text')(Text, TextEditConfig);
    MapTo('wknd-spa/components/image')(Image, ImageEditConfig);
  2. Since there is no content for the imagecard component, add the card to the page. Include the existing container from AEM in the SPA.

    • If there is a container already in the AEM project, we can include this in the SPA instead and add the component to the container from AEM instead.
    • Ensure the card component is mapped to the corresponding resource type in the SPA.
    code language-javascript
     itemPath='root/responsivegrid' />
  3. Add the created wknd-spa/components/imagecard component to the allowed components for the container component in the page template.

Now the imagecard component can be directly added to the container in the AEM editor.

Composite card in the editor

The component and its required content both exist in your AEM project. both-exist

If the content exists in AEM, it can be directly included in the SPA by providing the path to the content.

    itemPath='root/responsivegrid/imagecard' />

Composite path in node structure

The AEMCard component is the same as defined in the previous use case. Here the content defined in the above location in the AEM project is included in the SPA.