Hello Friends, In this post you will see an example that helps you to implement pagination and search functionality in lightning datatable through client side. With this example you can create a lightning web component (lwc) including these features and also ability to show selected record during paging. So let's get started,

Client side pagination and search in lightning web component

In this example, you have create one apex class and one lwc. In the apex handler i am using account object records but you can use any objects as per your need.

AccountHandler.apxc

 public class AccountHandler {
@AuraEnabled(cacheable=true)
public static List<Account> getAccounts(String searchKey, String sortBy, String sortDirection) {
String query = 'SELECT Id, Name,Phone,Type FROM Account';
if (String.isNotEmpty(searchKey)) {
String key = '%' + searchKey + '%';
query += ' WHERE Name LIKE :key';
}
if (String.isNotEmpty(sortBy) && String.isNotEmpty(sortDirection)){
query += ' ORDER BY ' + sortBy + ' ' + sortDirection;
}
return Database.query(query);
}
}
ClientSidePagination.html
 <template>
<template if:true={loader}>
<lightning-spinner alternative-text="Loading..." size="small"></lightning-spinner>
</template>
<lightning-layout>
<lightning-layout-item padding="around-small" size="4">
<lightning-combobox style="width:80px;" name="StageName" label="Show Entries" value={pageSize} options={options}
onchange={handleChange}></lightning-combobox>
</lightning-layout-item>
<lightning-layout-item style="padding-top: 34px;" size="4">
<center>
<lightning-button icon-name="utility:desktop" label="Show Selected Account" variant="brand" onclick={showSelectedAccounts}></lightning-button>
</center>
</lightning-layout-item>
<lightning-layout-item padding="around-small" size="4">
<lightning-input type="search" onchange={handleKeyChange} class="slds-m-bottom_small" label="Search"
value={searchKey} placeholder="Type Account Name..">
</lightning-input>
</lightning-layout-item>
</lightning-layout>
<lightning-card title="Client Side Pagination With Search" icon-name="standard:account">
<lightning-datatable data-id="table" key-field="Id" data={data} columns={columns} sorted-by={sortedBy}
sorted-direction={sortedDirection} onrowselection={onRowSelection} selected-rows={allSelectedRows}
onsort={sortColumns}>
</lightning-datatable>
<div class="slds-m-around_medium">
<center>
<lightning-button label="Previous" variant="brand" icon-name="utility:chevronleft" onclick={previousHandler}>
</lightning-button>
&nbsp; Page {page} of {totalPage} &nbsp;
<lightning-button label="Next" variant="brand" icon-name="utility:chevronright" icon-position="right"
onclick={nextHandler}>
</lightning-button>
</center>
</div>
</lightning-card>

<template if:true={isModalOpen}>
<!-- Modal/Popup Box LWC starts here -->
<section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
<div class="slds-modal__container">
<!-- Modal/Popup Box LWC header here -->
<header class="slds-modal__header">
<button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close" onclick={closeModal}>
<lightning-icon icon-name="utility:close" alternative-text="close" variant="inverse" size="small" ></lightning-icon>
<span class="slds-assistive-text">Close</span>
</button>
<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Selected Account List</h2>
</header>
<!-- Modal/Popup Box LWC body starts here -->
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
<p>
<lightning-datatable data-id="SelectedAcctable" key-field="Id" data={allSelectedRows} columns={columns}
hide-checkbox-column="false" show-row-number-column="true">
</lightning-datatable>
</p>
</div>
<!-- Modal/Popup Box LWC footer starts here -->
<footer class="slds-modal__footer">
<button class="slds-button slds-button_destructive" onclick={closeModal} title="Close">Close</button>
</footer>
</div>
</section>
<div class="slds-backdrop slds-backdrop_open"></div>
</template>
</template>
ClientSidePagination.js
 import { LightningElement, wire, api, track } from 'lwc';
import { refreshApex } from '@salesforce/apex';
import getAccounts from '@salesforce/apex/AccountHandler.getAccounts';
const columns = [
{ label: 'Name', fieldName: 'Name', type: 'text', sortable: true },
{ label: 'Phone', fieldName: 'Phone', sortable: true },
{ label: 'Type', fieldName: 'Type', sortable: true }
];
export default class ClientSidePagination extends LightningElement {
@track loader = false;
@track isModalOpen = false;
@track value;
@track error;
@track data;
@api sortedDirection = 'asc';
@api sortedBy = 'Name';
@api searchKey = '';
result;
@track allSelectedRows = [];
@track page = 1;
@track items = [];
@track data = [];
@track columns;
@track startingRecord = 1;
@track endingRecord = 0;
@track pageSize = '5';
@track totalRecountCount = 0;
@track totalPage = 0;
isPageChanged = false;
initialLoad = true;
mapAccount = new Map();
get options() {
return [
{ label: '5', value: '5' },
{ label: '10', value: '10' },
{ label: '15', value: '15' },
];
}

handleChange(event) {
this.pageSize = event.detail.value;
this.processRecords(this.items);
}

@wire(getAccounts, { searchKey: '$searchKey', sortBy: '$sortedBy', sortDirection: '$sortedDirection' })
wiredAccounts({ error, data }) {
this.loader = true;
if (data) {
this.loader = false;
this.processRecords(data);
this.error = undefined;
} else if (error) {
this.loader = false;
this.error = error;
this.data = undefined;
}
}

processRecords(data) {
this.items = data;
this.totalRecountCount = data.length;
this.totalPage = Math.ceil(this.totalRecountCount / this.pageSize);
this.data = this.items.slice(0, this.pageSize);
this.endingRecord = this.pageSize;
this.columns = columns;
}

//clicking on previous button this method will be called
previousHandler() {
this.isPageChanged = true;
if (this.page > 1) {
this.page = this.page - 1; //decrease page by 1
this.displayRecordPerPage(this.page);
}
var selectedIds = [];
for (var i = 0; i < this.allSelectedRows.length; i++) {
selectedIds.push(this.allSelectedRows[i].Id);
}
this.template.querySelector('[data-id="table"]').selectedRows = selectedIds;
}

//clicking on next button this method will be called
nextHandler() {
this.isPageChanged = true;
if ((this.page < this.totalPage) && this.page !== this.totalPage) {
this.page = this.page + 1; //increase page by 1
this.displayRecordPerPage(this.page);
}
var selectedIds = [];
for (var i = 0; i < this.allSelectedRows.length; i++) {
selectedIds.push(this.allSelectedRows[i].Id);
}
this.template.querySelector('[data-id="table"]').selectedRows = selectedIds;
}

//Method to displays records page by page
displayRecordPerPage(page) {
this.startingRecord = ((page - 1) * this.pageSize);
this.endingRecord = (this.pageSize * page);
this.endingRecord = (this.endingRecord > this.totalRecountCount) ? this.totalRecountCount : this.endingRecord;
this.data = this.items.slice(this.startingRecord, this.endingRecord);
this.startingRecord = this.startingRecord + 1;
}

sortColumns(event) {
this.sortedBy = event.detail.fieldName;
this.sortedDirection = event.detail.sortDirection;
return refreshApex(this.result);
}

handleKeyChange(event) {
this.searchKey = event.target.value;
var data = [];
for (var i = 0; i < this.items.length; i++) {
if (this.items[i] != undefined && this.items[i].Name.includes(this.searchKey)) {
data.push(this.items[i]);
}
}
this.processRecords(data);
}

onRowSelection(event) {
if (!this.isPageChanged || this.initialLoad) {
if (this.initialLoad) this.initialLoad = false;
this.processSelectedRows(event.detail.selectedRows);
} else {
this.isPageChanged = false;
this.initialLoad = true;
}

}

processSelectedRows(selectedAccounts) {
var newMap = new Map();
for (var i = 0; i < selectedAccounts.length; i++) {
if (!this.allSelectedRows.includes(selectedAccounts[i])) {
this.allSelectedRows.push(selectedAccounts[i]);
}
this.mapAccount.set(selectedAccounts[i].Name, selectedAccounts[i]);
newMap.set(selectedAccounts[i].Name, selectedAccounts[i]);
}
for (let [key, value] of this.mapAccount.entries()) {
if (newMap.size <= 0 || (!newMap.has(key) && this.initialLoad)) {
const index = this.allSelectedRows.indexOf(value);
if (index > -1) {
this.allSelectedRows.splice(index, 1);
}
}
}

}

showSelectedAccounts() {
if (this.allSelectedRows != null && this.allSelectedRows.length > 0) {
this.isModalOpen = true;
}
else {
alert('Please select account record..!!');
}
}

closeModal() {
this.isModalOpen = false;
}
}
ClientSidePagination.js-meta.xml
 <?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>

Output:


Hope you like this post, for any feedback or suggestions please feel free to comment. I would appreciate your feedback and suggestions.
Thank you.