Adobe

Creating Your First Custom AEM Component Using React – Part 1

Lee Campbell 6njoebtarec Unsplash

Recently, I went through an article about integrating React JS and Angular JS with AEM. In this blog, I am going to show you how to create a custom component that includes a cq:dialog and one that does not include a cq:dialog. Before building the components, clone the repository, which is a sample project based on React JS.
First, we will deploy this project in AEM 6.5. Then, we will create one sample component called custom-heading.
The component structure should look like this.
1. Component node (<project root>/apps/my-aem-project/components/content/custom-heading)

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="Custom Heading"
    componentGroup="My AEM Project"/>

2. cq:dialog

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
    xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0"
    xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Custom Heading"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <general
                        jcr:primaryType="nt:unstructured"
                        jcr:title="General"
                        sling:resourceType="granite/ui/components/coral/foundation/container"
                        margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <heading
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                fieldLabel="Heading"
                                                name="./heading"
                                                required="{Boolean}true"/>
                                            <heading-type
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/select"
                                                fieldLabel="Heading Type"
                                                name="./headingType">
                                                <items jcr:primaryType="nt:unstructured">
                                                    <h1
                                                        jcr:primaryType="nt:unstructured"
                                                        text="H1"
                                                        value="h1"/>
                                                    <h2
                                                        jcr:primaryType="nt:unstructured"
                                                        text="H2"
                                                        value="h2"/>
                                                    <h3
                                                        jcr:primaryType="nt:unstructured"
                                                        text="H3"
                                                        value="h3"/>
                                                </items>
                                            </heading-type>
                                            <heading-color
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/select"
                                                fieldLabel="Heading Color"
                                                name="./headingColor">
                                                <items jcr:primaryType="nt:unstructured">
                                                    <red
                                                        jcr:primaryType="nt:unstructured"
                                                        text="Red"
                                                        value="red-color"/>
                                                    <green
                                                        jcr:primaryType="nt:unstructured"
                                                        text="Green"
                                                        value="green-color"/>
                                                    <blue
                                                        jcr:primaryType="nt:unstructured"
                                                        text="Blue"
                                                        value="blue-color"/>
                                                </items>
                                            </heading-color>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </general>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

Now the question is why have I not created custom-heading.html? For a React-based component, we are not going to create any custom-heading.html. Also, we are not going to use HTL/Sightly to render logic. Surprising, right? We will render the component HTML using React. Therefore, I have created a React component called CustomHeading.
In the project you’ve cloned, create the CustomHeading (<project root>/react-app/src/components/CustomHeading) folder and create the following two files.
The react component structure should look like this.
1. CustomHeading.js

import React, {Component} from 'react';
import {MapTo} from '@adobe/cq-react-editable-components';
require('./CustomHeading.scss');
const CustomHeadingEditConfig = {
    emptyLabel: 'Custom Heading',
    isEmpty: function(props) {
        return !props || props.heading.trim().length < 1;
    }
};
export default class CustomHeading extends Component {
    render() {
            return (<div className="heading"> First Component </div>);
    }
}
MapTo('my-aem-project/components/content/custom-heading')(CustomHeading, CustomHeadingEditConfig);

2. CustomHeading.scss

.red-color{
  color: #fc0b03;
}
.green-color{
color : #39fc03;
}
.blue-color{
color : #2403fc;
}
Adobe - Content for Everyone
Content for Everyone

Companies that can quickly and consistently meet the demands of consumers are thriving in an era of infinite content. Learn about how to build fluid experiences for your omnichannel customers.

Get the Guide

Now, the question is still how do you read the authored value in a React component? For this, we need a Sling Model exporter. I have created one Sling Model exporter class.

The Sling Model will look like this.
CustomHeadingModel.java

package com.surajkamdi.core.models;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import javax.annotation.Nonnull;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
@Model(adaptables = SlingHttpServletRequest.class,
    resourceType = CustomHeadingModel.RESOURCE_TYPE,
    adapters = {CustomHeadingModel.class, ComponentExporter.class},
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class CustomHeadingModel implements ComponentExporter {
  protected static final String RESOURCE_TYPE = "my-aem-project/components/content/custom-heading";
  @ValueMapValue(name = "heading")
  private String heading;
  @ValueMapValue(name = "headingType")
  private String headingType;
  @ValueMapValue(name = "headingColor")
  private String headingColor;
  public String getHeading() {
    return heading;
  }
  public String getHeadingType() {
    return headingType;
  }
  public String getHeadingColor() {
    return headingColor;
  }
  @Nonnull
  @Override
  public String getExportedType() {
    return RESOURCE_TYPE;
  }
}

Run the build and deploy using the following Maven commands:

mvn clean install -PautoInstallPackage -Padobe-public

Now let’s add this component into the page and do some authoring, then get the resource path of component. Open a new tab in the browser window and paste the following URL constructed using resource path in the following format:
http://localhost:4502/<content_root_path>/custom_heading.model.json 

CustomheadercomponentjsonYou will notice authored values are also present here in the above structure. This is the output rendered by the above Sling Model exporter class.
In order to add this component into the page created in AEM, we need to import the component inside file called <project root>/react-app/src/ Index.js. If you are using the above-mentioned repository structure, then you need to include a component path inside MappedComponents.js. Otherwise, you will not able to add a component into the page.
MappedComponets.js

require('./CustomHeading/CustomHeading');

Finally, the last step is to read the above JSON in the React component and render the logic to achieve component functionality.
CustomHeading.js

import React, {Component} from 'react';
import {MapTo} from '@adobe/cq-react-editable-components';
require('./CustomHeading.scss');
const CustomHeadingEditConfig = {
    emptyLabel: 'Custom Heading',
    isEmpty: function(props) {
        return !props.heading || props.heading.trim().length < 1;
    }
};
export default class CustomHeading extends Component {
    render() {
      let headingElement = this.props.headingType ? React.createElement(this.props.headingType, {className: this.props.headingColor},this.props.heading) : '';
      return (<div className="heading"> {headingElement} </div>);
    }
}
MapTo('my-aem-project/components/content/custom-heading')(CustomHeading, CustomHeadingEditConfig);

Now, deploy your code using Maven commands.

mvn clean install -PautoInstallPackage -Padobe-public

Custom Aem React Component Surajkamdi
I hope this blog post was helpful. In the next blog, I will create a component without using cq:dialog.
If you are still confused about working with the React component, visit here or comment below.

About the Author

Suraj is Adobe Certified - Adobe Experience Manager Sites Developer & DevOps Engineer.

More from this Author

Thoughts on “Creating Your First Custom AEM Component Using React – Part 1”

  1. hi really great articles. how can i make another component lets say, i want to create a new test component for example bassed on CustomHeading component. i do these step but my component didnt show in crxde or in the editor.html. so where im wrong? i already create testingmodel.java

  2. Hello,
    Could you please share the git repository URL, So that I can take a look at it. else you can open a question on StackOverflow and include all the details and share a link with me.
    I am happy to help you…
    Thanks & Regards,
    Suraj Kamdi

  3. Hi Suraj, I have a great doubt. what if the AEM version is below 6.4 eg: 6.1/6.2 then how do you integrate react with AEM?

  4. Hugo Manuel Moreira Meireles

    Hi Suraj,
    Nice article.
    it is possible to a create react component(authoring) with 2 children core component?
    Example:
    banner component – have the image component where I can change it in authoring mode and text component with the same behavior.

  5. Suraj Kamdi Post author

    Hello Satish,
    First of all, You need to create one Sling model for your component. I have justified my answer using the following example.

    @Model(adaptables = SlingHttpServletRequest.class,
    resourceType = CustomSlingModel.RESOURCE_TYPE,
    adapters = {CustomSlingModel.class, ComponentExporter.class},
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
    @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
    public class CustomSlingModelimplements ComponentExporter {
    // Read the childResouce for multifield resource using @ChildResource private Resource multifieldResourceName;
    // and then create a ArrayList of POJO class for multifield data items by iterating childNodes of multifieldResource.
    List multifiledList;
    }

    Once you are done with the Sling model then provide the exact resource type (as same as provided in sling model) to your react component using MapTo().
    After that consume the multifiledList values from the generated model.json file and render your logic for your multifield component using map() function

    See this link for more details.

    Please let me know if you need any help. I am happy to help 🙂

    PS. This solution applies only to react component.
    Thanks & Regards,
    Suraj Kamdi

  6. hi Suraj,

    Could you please provide and example with code for handling the multi-fields using react components?

  7. Hi Suraj

    Really great article. I am beginner of AEM and React your article is very clear and crisp that I created my own component following the steps you have explained.

    May I know what is the aem archetype you have used for building the project.

    Thanks in advance

    Elena

  8. hi Suraj,

    Could you please show an example to render a multi-field properties in model.json using json extractor?

  9. Suraj Kamdi Post author

    Hello Kumar,
    See my above comment

    See this example for rendering the multifield of List Component.

    Please let me know if you need any help. I am happy to help 🙂
    Thanks & Regards,
    Suraj Kamdi

  10. hi Suraj,

    I am asking here about the json extraction of multifield component not the react code.

  11. Suraj Kamdi Post author

    Hello Kumar,
    Here is the example to create a sling model to extract the JSON from multifield Items.
    URL: https://gist.github.com/Surajkamdi/6d51779d62278b56a747e04532f39036
    I hope this will help to sort out your queries related to the JSON extraction of the multifield component.

    Please let me know if you need more help with this… I am Happy to Help 🙂

    Please connect me on LinkedIn: https://www.linkedin.com/in/suraj-kamdi-95a80194/

    Thanks & Regards,
    Suraj Kamdi

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to the Weekly Blog Digest:

Sign Up
Categories