Creating Your First Custom AEM Component Using React – Part 1 | Perficient Digital

Creating Your First Custom AEM Component Using React – Part 1

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;
}

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 

You 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

AEM Component Using React

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.

2 responses to “Creating Your First Custom AEM Component Using React – Part 1”

  1. suit says:

    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. Suraj Kamdi says:

    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

Leave a Reply