Build Custom Carousel in Lightning Aura Components

Carousels are a great way to display multiple pieces of information in a compact and interactive format. If you are looking to display multiple records interactively in Salesforce, building a Custom Carousel in Lightning Aura Components is a powerful and flexible solution.

Use case of Custom Carousel

Let’s say we want to display list of Account records, grouped and paginated in a card-based carousel. Each “page” in carousel will show a few records at a time, and users can navigate through them using left/right buttons.

Final UI Overview

  • Each record is displayed inside a lightning:card
  • Navigation buttons allow sliding left or right.
  • Smooth slide animations are present on click of next and previous button
  • Six records per page of carousel.

Component Setup

Aura Component Markup

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" controller="CustomCarouselController">
    <aura:attribute name="currentIndex" type="Integer" default="0"/>
    <aura:attribute name="items" type="List"/>
    <aura:attribute name="accounts" type="List"/>
    <aura:attribute name="recordPerPage" type="Integer" default="6"/>
    <aura:attribute name="disableNext" type="Boolean" default="false"/>
    <aura:attribute name="disablePrevious" type="Boolean" default="false"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <div class="carousel-container">
        <div aura:id="carouselInner" class="carousel-inner">
            <lightning:layout pullToBoundary="medium" multipleRows="true">
                <aura:iteration items="{!v.accounts}" var="item">
                    <aura:if isTrue="{!and(item.Id != '', item.Id != null)}">
                        <lightning:layoutItem flexibility="auto" padding="around-small" size="4">
                            <lightning:card title="{!item.Name}">
                                <p class="slds-p-horizontal_small">
                                    <b>Id</b> : {!item.Id}
                                    <br/>
                                    <b>Industry</b> : {!item.Industry}
                                </p>
                            </lightning:card>
                        </lightning:layoutItem>
                    </aura:if>
                </aura:iteration>
            </lightning:layout>
        </div>
        <div class="carousel-nav">
            <lightning:buttonIcon iconName="utility:left" onclick="{!c.previous}" alternativeText="Previous" variant="Brand" disabled="{!v.disablePrevious}"/>
            <lightning:buttonIcon iconName="utility:right" onclick="{!c.next}" alternativeText="Next" variant="Brand" disabled="{!v.disableNext}"/>
        </div>
    </div>
</aura:component>

In the component file, use lightning:layout which is a flexible grid and gutter system for arranging component containers. Create a content within lightning:layout by including lightning:layoutItem

We are using items attribute to store all account records similarly, accounts attribute is used to display accounts on a carousel page.

JavaScript Controller

({
    doInit : function(component, event, helper) {
        debugger;
        helper.getDoInitData(component, event, helper);
    },

    next : function(component, event, helper) {
        debugger;
        var carouselEl = component.find("carouselInner");
        var el = carouselEl.getElement();
        
        var currentIndex = parseInt(component.get("v.currentIndex"));
        var recordPerPage = parseInt(component.get("v.recordPerPage"));
        var noOfRecords = component.get("v.items");
        var records = [];
        component.set("v.accounts",records);
        for(var i = currentIndex; i < recordPerPage + currentIndex ;i++){
            if(parseInt(component.get("v.currentIndex")) <= noOfRecords.length){
                records.push(component.get("v.items")[i]);
                component.set("v.currentIndex",component.get("v.currentIndex") + 1);
            }
            else{
                component.set("v.disableNext",true);
            }
        }
        el.classList.remove("slide-right");
        el.classList.add("slide-left");
        
        component.set("v.accounts",records);
        component.set("v.disablePrevious",false);
        window.setTimeout(function() {
            el.classList.remove("slide-left");
        }, 600);
    },

    previous : function(component, event, helper) {
        debugger;
        var carouselEl = component.find("carouselInner");
        var el = carouselEl.getElement();
        
        var currentIndex = parseInt(component.get("v.currentIndex"));
        var recordPerPage = parseInt(component.get("v.recordPerPage"));
        var noOfRecords = component.get("v.items");
        var records = [];
        alert(currentIndex);
        component.set("v.accounts",records);
        for(var i = currentIndex - (2 *recordPerPage) ; i < (currentIndex - recordPerPage) ;i++){
            if((parseInt(component.get("v.currentIndex")) != 0)){
                records.push(component.get("v.items")[i]);
                component.set("v.currentIndex",component.get("v.currentIndex") - 1);
            }
            if( i == 0 ){
                component.set("v.disablePrevious",true);
            }
        }
        var carouselEl = component.find("carouselInner");
        var el = carouselEl.getElement();
        
        el.classList.remove("slide-left");
        el.classList.add("slide-right");
        component.set("v.accounts",records);
        component.set("v.disableNext",false);
        
        window.setTimeout(function() {
            el.classList.remove("slide-right");
        }, 600);
    }
})
  • currentIndex: It will help you to identify previous and next records. It is equivalent to number of records displayed on UI.
  • recordPerPage: It is hardcoded to six. six accounts will be displayed per page.
  • noOfRecords: It holds all accounts

Onclick of next button for first time, currentIndex value is six. for loop iteration will start from index six till eleven of whole accounts (currentIndex + recordPerPage i.e. 12), it will store each record from index six to eleven in records variable at the same time, currentIndex value increase by 1. For, next screen currentIndex value will be 12. Onclick of previous button, value of i will be zero because, we want to return to screen 1 means initial six records.

Let’s suppose there are 24 accounts value of each variable at end of each screen will be as follows:

ScreenCurrent IndexFor loop iteration on click of next buttonFor loop iteration on click of previous button
1 (loaded initially)66 to 11Previous button disable
21212 to 170 to 5
31818 to 236 to 11
424Next button disable12 to 18

JavaScript Helper

({
	getDoInitData : function(component, event, helper) {
        debugger;
		var action = component.get("c.getAccount");
        action.setCallback(this, function(res){
            var state = res.getState();
            //component.set("v.accounts",res.getReturnValue());
            if(state === "SUCCESS"){
                component.set("v.items",res.getReturnValue());
                var currentIndex = component.get("v.currentIndex");
                var recordPerPage = component.get("v.recordPerPage");
                var records = [];
                for(var i = 0; i < recordPerPage ;i++){
                    records.push(res.getReturnValue()[i]);
                    //component.set("v.currentIndex",currentIndex ++);
                    currentIndex = currentIndex + 1;
                }
				component.set("v.accounts",records); 
                component.set("v.currentIndex",currentIndex); 
                component.set("v.disablePrevious",true);
            }
            else if(state === "ERROR"){
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        // log the error passed in to AuraHandledException
                        console.log("Error message: " + errors[0].message);
                    }
                } else {
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);
	}
})

CSS Styling

.THIS .carousel-container {
    position: relative;
    overflow: hidden;
    width: 100%;
    max-width: 500px;
    margin: 0 auto;
}

.THIS .carousel-inner {
    display: flex;
    transition: transform 0.5s ease-in-out;
    padding: 20px 50px 20px 50px;
}

.THIS .carousel-nav {
    position: absolute;
    top: 50%;
    width: 100%;
    display: flex;
    justify-content: space-between;
    transform: translateY(-50%);
    padding-bottom: 50px;
}

.THIS .carousel-nav lightning-buttonIcon {
    background-color: #f4f6f9;
    border-radius: 50%;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.THIS .slide-left {
    animation: slideLeft 0.5s ease forwards;
}

.THIS .slide-right {
    animation: slideRight 0.5s ease forwards;
}

@keyframes slideLeft {
    0% {
        transform: translateX(100%);
        opacity: 0.5;
    }
    100% {
        transform: translateX(0);
        opacity: 1;
    }
}

@keyframes slideRight {
    0% {
        transform: translateX(-100%);
        opacity: 0.5;
    }
    100% {
        transform: translateX(0);
        opacity: 1;
    }
}

Output

Custom Carousel in Lightning Aura Component

Try replacing Account with any custom or standard object. You can also migrate this to Lightning Web Components for modern patterns and better performance.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *