Displaying hundreds of records in a lightning-datatable is common in Salesforce apps. To deliver a fast, responsive user experience, you can implement instant client-side filtering. By maintaining an immutable master dataset alongside a reactive filtered array, your component searches across multiple fields seamlessly.
Prerequisites
- An active Salesforce Developer Edition, Sandbox, or Scratch Org.
- Salesforce CLI (
sf) installed and authenticated. - Basic understanding of JavaScript array methods (
Array.prototype.filter()andString.prototype.includes()).
Step 1: Set Up the Component Structure
sf lightning generate component -n datatableSearchFilter -d force-app/main/default/lwc --type lwc
Step 2: Build the HTML Template
In datatableSearchFilter.html, place a lightning-input search field above the lightning-datatable. The table binds directly to the reactive filteredData property:
<template>
<lightning-card title="Account Directory with Instant Search" icon-name="standard:account">
<div class="slds-p-around_medium">
<!-- Search Bar -->
<div class="slds-m-bottom_medium slds-size_1-of-1 slds-medium-size_1-of-3">
<lightning-input
type="search"
label="Search Records"
placeholder="Search by name, industry, or phone..."
value={searchKey}
onchange={handleSearch}>
</lightning-input>
</div>
<!-- Datatable -->
<lightning-datatable
key-field="id"
data={filteredData}
columns={columns}
hide-checkbox-column>
</lightning-datatable>
<!-- No Records Found Message -->
<template lwc:if={isNoData}>
<p class="slds-text-align_center slds-text-color_weak slds-m-top_medium">
No matching records found.
</p>
</template>
</div>
</lightning-card>
</template>
Step 3: Implement the Client-Side Search Logic
In datatableSearchFilter.js, store the full source list in a master array (masterData). When the input changes, execute a case-insensitive multi-field match to update filteredData:
import { LightningElement } from 'lwc';
const COLUMNS = [
{ label: 'Account Name', fieldName: 'name', type: 'text' },
{ label: 'Industry', fieldName: 'industry', type: 'text' },
{ label: 'Phone', fieldName: 'phone', type: 'phone' },
{ label: 'City', fieldName: 'city', type: 'text' }
];
export default class DatatableSearchFilter extends LightningElement {
columns = COLUMNS;
masterData = [];
filteredData = [];
searchKey = '';
get isNoData() {
return this.filteredData.length === 0;
}
connectedCallback() {
// Sample dataset (or fetch via Apex / Wire service)
this.masterData = [
{ id: '1', name: 'Acme Corp', industry: 'Manufacturing', phone: '555-0100', city: 'San Francisco' },
{ id: '2', name: 'Global Media Inc', industry: 'Media', phone: '555-0101', city: 'New York' },
{ id: '3', name: 'Cloud Kicks', industry: 'Apparel', phone: '555-0102', city: 'Austin' },
{ id: '4', name: 'Apex Tech Solutions', industry: 'Technology', phone: '555-0103', city: 'Seattle' },
{ id: '5', name: 'Pacific Logistics', industry: 'Transportation', phone: '555-0104', city: 'Los Angeles' }
];
// Initialize table with the full dataset
this.filteredData = [...this.masterData];
}
handleSearch(event) {
this.searchKey = event.target.value.trim().toLowerCase();
if (this.searchKey) {
this.filteredData = this.masterData.filter(record => {
const nameMatch = record.name?.toLowerCase().includes(this.searchKey);
const industryMatch = record.industry?.toLowerCase().includes(this.searchKey);
const phoneMatch = record.phone?.toLowerCase().includes(this.searchKey);
const cityMatch = record.city?.toLowerCase().includes(this.searchKey);
return nameMatch || industryMatch || phoneMatch || cityMatch;
});
} else {
// Restore complete list when search box is cleared
this.filteredData = [...this.masterData];
}
}
}
masterData and assign the result to filteredData. If you filter data in place, clearing the search box will leave you with an empty or truncated list.
- Client-Side Filtering: Instant search without SOQL queries or Apex governor limit consumption.
- Modern Communication: For cross-component filtering, use standard Custom Events (
dispatchEvent) or the Lightning Message Service (LMS) instead of legacy pubsub hacks. - Safe Navigation: Use optional chaining (
record.field?.toLowerCase()) to prevent null pointer errors on empty field values.
Step 4: Configure Metadata and Deploy
Update datatableSearchFilter.js-meta.xml to expose the component to Lightning App Builder:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>60.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
- Deploy to your org:
sf project deploy start. - In Salesforce Setup, navigate to Lightning App Builder.
- Drop
datatableSearchFilteronto your target page, save, and activate. - Type in the search bar to verify instant multi-column filtering.