Skip to main content

How to Build a Dynamic Search Filter in LWC Datatable (Modern Client-Side Search)

In plain words: A dynamic search filter in an LWC datatable instantly narrows down visible rows in real time as the user types, filtering through the loaded dataset on the client side without making repeated round-trip calls to Salesforce Apex.

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() and String.prototype.includes()).

Step 1: Set Up the Component Structure

Generate Component via CLI:
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];
        }
    }
}
Warning Trap: Never mutate the original master dataset when filtering. Always filter from 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.
360 Architecture Summary:
  • 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>
Deployment & Testing:
  • Deploy to your org: sf project deploy start.
  • In Salesforce Setup, navigate to Lightning App Builder.
  • Drop datatableSearchFilter onto your target page, save, and activate.
  • Type in the search bar to verify instant multi-column filtering.
Core Takeaway: Client-side datatable filtering eliminates server latency, respects Apex governor limits, and delivers an instant search experience across loaded records.