How to develop a Dynamic Lead Grid in Salesforce LWC with Apex and Filters?

In this article, we’ll walk through how to create a dynamic lead grid view in Salesforce using Lightning Web Components (LWC) and Apex. The goal is to display a list of leads in a grid format with filtering options, clickable links, and a professional user interface using the Salesforce Lightning Design System (SLDS).

By the end of this guide, you’ll have a working LWC component that:

  • Fetches lead data using an Apex wire method
  • Displays the leads in a responsive grid format
  • Allows users to filter leads by status
  • Enables navigation to the lead record from the grid
  • Improves UI with consistent padding, hover effects, and currency formatting

This is how the final output will look like.

Let’s learn how to develop the Lead Grid View Component using Apex, Lightning Web Components (LWC) & SLDS.

First, we need to create an Apex class that retrieves lead data based on the selected status.

Apex Controller

public with sharing class LeadController {
    @AuraEnabled(cacheable=true)
    public static List<Lead> getLeads(String status) {
        String query = 'SELECT Id, Name, Email, Company, AnnualRevenue, Status FROM Lead';
        if (status != null && status != '') {
            query += ' WHERE Status = :status';
        }
        return Database.query(query);
    }
}

In above apex code we used Database.query method to retrieve the leads record based on the status selected by user by default all lead records will be fetched.

Now let’s have a look at the code of Lightning Web Component (LWC) code.

Lightning Web Component

import { LightningElement, wire, track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import getLeads from '@salesforce/apex/LeadController.getLeads';

export default class LeadGrid extends NavigationMixin(LightningElement) {
    @track leads; // Holds the list of leads
    @track error;
    @track selectedStatus = '';

    // Wire method to fetch leads based on the selected status
    @wire(getLeads, { status: '$selectedStatus' })
    wiredLeads({ error, data }) {
        if (data) {
            // Format data and handle missing fields
            this.leads = data.map(lead => ({
                ...lead,
                AnnualRevenueFormatted: this.formatCurrency(lead.AnnualRevenue),
                EmailDisplay: lead.Email || 'N/A',
                CompanyDisplay: lead.Company || 'N/A'
            }));
            this.error = undefined;
        } else if (error) {
            this.error = error;
            this.leads = undefined;
        }
    }

    // Handle status change event from the combobox
    handleStatusChange(event) {
        this.selectedStatus = event.target.value;
    }

    // Navigate to the record page when the user clicks on the name
    navigateToRecordViewPage(event) {
        const recordId = event.currentTarget.dataset.id;
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: recordId,
                objectApiName: 'Lead',
                actionName: 'view'
            }
        });
    }

    // Format currency with dollar sign and commas
    formatCurrency(value) {
        if (value) {
            return `$${Number(value).toLocaleString()}`;
        }
        return 'N/A';
    }

    // Options for the filter combobox
    get statusOptions() {
        return [
            { label: 'All', value: '' },
            { label: 'Open', value: 'Open - Not Contacted' },
            { label: 'Working', value: 'Working - Contacted' },
            { label: 'Closed - Converted', value: 'Closed - Converted' },
            { label: 'Closed - Not Converted', value: 'Closed - Not Converted' }
        ];
    }
}

The JavaScript code handles data fetching, filtering, and navigation.

<template>
    <lightning-card title="Lead Information" icon-name="standard:lead">
        <!-- Status Filter -->
        <div class="slds-m-around_medium">
            <lightning-combobox
                label="Filter by Status"
                value={selectedStatus}
                options={statusOptions}
                onchange={handleStatusChange}
            ></lightning-combobox>
        </div>

        <!-- Lead Grid -->
        <div class="slds-grid slds-wrap slds-gutters">
            <template if:true={leads}>
                <template for:each={leads} for:item="lead">
                    <div key={lead.Id} class="slds-col slds-size_1-of-3">
                        <div class="lead-card slds-box slds-theme_default slds-m-around_small">
                            <div class="slds-p-around_medium">
                                <!-- Lead Name -->
                                <div class="icon-row">
                                    <lightning-icon icon-name="standard:contact" size="small"></lightning-icon>
                                    <strong>Name:</strong>
                                    <a 
                                        href="javascript:void(0);" 
                                        data-id={lead.Id} 
                                        onclick={navigateToRecordViewPage}
                                        class="lead-name"
                                    >
                                        {lead.Name}
                                    </a>
                                </div>

                                <!-- Email -->
                                <div class="icon-row">
                                    <lightning-icon icon-name="standard:email" size="small"></lightning-icon>
                                    <strong>Email:</strong> {lead.EmailDisplay}
                                </div>

                                <!-- Company -->
                                <div class="icon-row">
                                    <lightning-icon icon-name="standard:account" size="small"></lightning-icon>
                                    <strong>Company:</strong> {lead.CompanyDisplay}
                                </div>

                                <!-- Annual Revenue -->
                                <div class="icon-row">
                                    <lightning-icon icon-name="utility:money" size="small"></lightning-icon>
                                    <strong>Annual Revenue:</strong> {lead.AnnualRevenueFormatted}
                                </div>

                                <!-- Status Badge -->
                                <div class="status-badge">
                                    <lightning-badge label={lead.Status}></lightning-badge>
                                </div>
                            </div>
                        </div>
                    </div>
                </template>
            </template>
            <template if:true={error}>
                <div class="slds-p-around_medium">
                    <p class="slds-text-color_error">{error}</p>
                </div>
            </template>
            <template if:false={leads}>
                <div class="slds-p-around_medium">
                    <p>No leads found for the selected status.</p>
                </div>
            </template>
        </div>
    </lightning-card>
</template>

The HTML file defines the layout and structure of the component.

.lead-card {
    border: 1px solid #d8dde6;
    border-radius: 8px;
    transition: box-shadow 0.2s ease-in-out;
    background-color: #ffffff;
}

.lead-card:hover {
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}

.lead-name {
    color: #0070d2;
    text-decoration: none;
    font-weight: bold;
    cursor: pointer;
}

.lead-name:hover {
    text-decoration: underline;
}

/* Added padding between icon and text */
.icon-row {
    display: flex;
    align-items: center;
    gap: 8px; /* Padding between icon and text */
    margin-bottom: 6px;
}

.status-badge {
    margin-top: 10px;
}

lightning-badge {
    background-color: #f3f2f2;
    color: #0070d2;
    font-weight: bold;
    border-radius: 8px;
}

The CSS file ensures proper styling and alignment.

If you want us to explain the complete code from scratch then let us know in the comments below.

By following this guide, you’ve created a professional LWC that enhances lead management in Salesforce. You can also add buttons to edit, delete or convert a lead.

Leave a Comment