APEX
Simple Pagination in Lightning
When dealing with many records or in fact any list of records, its always a good practice to add some elements of pagination and limit the volume of records displayed.
This will optimise the performance and will reduce a chance of hitting limits when record volumes are high.
We can control the records displayed both from the client side and the server side.
But handling a large number of records on the client side seems to be consuming much CPU, so this would only be ideal for a small chunk of records – I like it till about 200 records depends on the size.
So Server side would be my go to if I’m planning to deal with larger number of records.
Apex offers a Simple way to paginate using SOQL statement with LIMIT and OFFSET clauses.
In Order to paginate we need to know 3 main elements :
- Total Number of Records
- Records per Page – How many records to be displayed on each page – To be set as the limit of records to retrieved.
- Index – to be used to offset the records on User paginate action.
On the Apex Controller I would run 2 Queries:
- Get Record count
- Get Records
private static final String OBJECT_NAME = 'Account';
private static final Set<String> FIELDS = new Set<String>{ 'Id', 'Name'};
@AuraEnabled(Cacheable=true)
public static Map<String,Object> getRecords( String pageSize, String offsetIndex ){
// Get Total records
String countQuery = 'SELECT count() FROM ' + OBJECT_NAME + ' LIMIT ' + RECORDS_LIMIT;
Integer recordsCount = Database.countQuery(countQuery);
// get Records
String recordsQuery = 'SELECT ' + String.join( new List<String>(FIELDS),',') + ' FROM ' + OBJECT_NAME + ' LIMIT :pageSize OFFSET :offsetIndex';
List<SObject> records = Database.query(recordsQuery);
return new Map<String,Object>{
'records' => records,
'totalCount' => recordsCount
};
}
On our Lightning Web component – Records Page will show the records
import { LightningElement, api, wire, track } from 'lwc';
import getRecords from '@salesforce/apex/MyController.getRecords';
export default class RecordsPage extends LightningElement {
offsetIndex = 0;
recordsPerPage = 5;
// wired records data
@wire(getRecords, {
'offsetIndex': '$offsetIndex',
'pageSize': '$recordsPerPage'
}) wiredResults({ data, error }) {
if(data){
this.records = data.records;
this.totalRecords = data.totalCount;
}
else if (error) {
console.error('error getting data', error );
}
}
onPaginate(event) {
this.recordsPerPage = parseInt(event.detail.perpage);
this.offsetIndex = parseInt(event.detail.index);
}
}
HTML – Parent
<template>
<template if:true={records.length} for:each={records} for:item="record" >
<div>{record.Name}</div>
</template>
<!-- PAGINATION -->
<c-pagination index={offsetIndex}
per-page={recordsPerPage}
total={totalRecords}
onpagechange={onPaginate}>
</c-pagination>
</template>
The pagination child component:
This component will simply display the control buttons for the pagination.

- Allow to paginate Next and Previous page
- Show Current Records per Page and Total Records
- Allow to switch the number of records per page
Basically to control the index when going Next/Prev we are simply adding or subtracting the number of records per page from our offset index.
- When we hit the Total Records => its the end.
- When Index is Zero => We are at the Start of our data records.
JS – #pagination.js
import { LightningElement, api } from 'lwc';
const PAGE_SIZE = ['5', '10', '15'];
export default class Pagination extends LightningElement {
@api index;
@api total;
@api
get perPage() {
return this.recordsPerPage;
}
set perPage(value) {
this.recordsPerPage = parseInt(value);
}
currentIndex = 0;
recordsPerPage = 5;
label = {
next: 'NEXT',
prev:'PREV'
}
get totalRecords() {
return this.total;
}
get isStart() {
return this.index === 0;
}
get isEnd() {
return (this.index + this.recordsPerPage) >= this.total;
}
get pageSizeOptions() {
let pageOptions = PAGE_SIZE.map(opt => ({ value: parseInt(opt), label: opt }));
// Add All records Option
pageOptions.push({
value: this.total,
label: 'ALL'
});
return pageOptions;
}
get currentPageSize() {
return parseInt(this.index + this.recordsPerPage) > this.total ? this.total : parseInt(this.index + this.recordsPerPage);
}
onChangePageSize(event) {
this.recordsPerPage = parseInt(event.detail.value);
this.publishPageChange();
}
onNextPage() {
this.currentIndex = this.index + this.recordsPerPage;
this.publishPageChange();
}
onPreviousPage() {
this.currentIndex = this.index - this.recordsPerPage > 0 ? this.index - this.recordsPerPage : 0;
this.publishPageChange();
}
publishPageChange() {
this.dispatchEvent( new CustomEvent('pagechange', {
detail: {
index: this.currentIndex,
perpage: this.recordsPerPage
},
}));
}
}
HTML – #pagination.html
<template>
<!-- PAGINATION -->
<div class="slds-grid slds-p-vertical_small">
<div>
<!-- PREV -->
<lightning-button label={label.prev} title={label.prev} name="prev"
variant="inverse" onclick={onPreviousPage} disabled={isStart}>
</lightning-button>
<!-- NEXT -->
<lightning-button class="slds-p-horizontal_small" label={label.next} title={label.next} name="next"
variant="inverse" onclick={onNextPage} disabled={isEnd}></lightning-button>
</div>
<div class="slds-align--absolute-center">
<span class="slds-text-title_bold"> {currentPageSize} / {totalRecords} </span>
</div>
<div class="slds-col_bump-left slds-size_2-of-12">
<!-- PAGE SIZE -->
<lightning-combobox class="slds-text-body_small"
dropdown-alignment="auto"
name="pageSize"
label="Page Size"
variant="label-hidden"
value={recordsPerPage}
options={pageSizeOptions}
onchange={onChangePageSize} ></lightning-combobox>
</div>
</div>
</template>


