Skip to main content

How to Build a Multi-Select Lookup Component in LWC (Step-by-Step)

Standard Salesforce input fields often fall short when users need to select multiple related records at once. Creating a custom multi-select lookup component in Lightning Web Components (LWC) delivers a sleek, flexible solution that drastically improves user experience.

In plain words: A multi-select lookup component in LWC lets users search for database records dynamically and pick multiple choices at once, storing them as selected tags or pills instead of forcing single-choice selections.

Prerequisites

To follow along with this tutorial smoothly, ensure you have:

  • Fundamental understanding of LWC, JavaScript, and HTML basics.
  • Salesforce DX CLI installed and configured locally.
  • An active Salesforce Developer Org or Scratch Org.

Step 1: Set Up the LWC Component

First, generate a new component in your workspace using the Salesforce CLI command below:

sfdx force:lightning:component:create -n MultiSelectLookup -d force-app/main/default/lwc

Open multiSelectLookup.js and add the foundational component structure with dynamic apex imports and toast handlers:

import { LightningElement, api, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import searchRecords from '@salesforce/apex/CustomController.searchRecords';

export default class MultiSelectLookup extends LightningElement {
    @api selectedRecords = [];
    @track searchResults = [];

    handleSearch(event) {
        const searchTerm = event.target.value;
        if (!searchTerm) {
            this.searchResults = [];
            return;
        }

        searchRecords({ searchTerm: searchTerm })
            .then((result) => {
                this.searchResults = result;
            })
            .catch((error) => {
                this.showToast('Error', error.body ? error.body.message : error.message, 'error');
            });
    }

    handleRecordSelection(event) {
        const selectedRecordId = event.currentTarget.dataset.recordId;
        const selectedRecord = this.searchResults.find(record => record.Id === selectedRecordId);

        if (selectedRecord && !this.selectedRecords.some(item => item.Id === selectedRecordId)) {
            this.selectedRecords = [...this.selectedRecords, selectedRecord];
        }
    }

    showToast(title, message, variant) {
        const toastEvent = new ShowToastEvent({
            title: title,
            message: message,
            variant: variant
        });
        this.dispatchEvent(toastEvent);
    }
}

Step 2: Implement the Apex Controller

Create an Apex class named CustomController that performs SOQL queries based on the user's search text.

Apex Controller Code Implementation:
public with sharing class CustomController {
    @AuraEnabled(cacheable=true)
    public static List<SObject> searchRecords(String searchTerm) {
        if (String.isBlank(searchTerm)) {
            return new List<SObject>();
        }
        String key = '%' + searchTerm + '%';
        return [SELECT Id, Name FROM Account WHERE Name LIKE :key LIMIT 10];
    }
}
Developer Trap: Always annotate your search Apex methods with @AuraEnabled(cacheable=true) when fetching query results for LWC. Omitting this prevents wire adapters or wire-like client performance optimization.

Step 3: Build the Search and Selection UI

Open multiSelectLookup.html and render the search input and the interactive list overlay:

<template>
    <lightning-card title="Multi-Select Lookup" icon-name="standard:search">
        <div class="slds-p-around_medium">
            <lightning-input 
                type="search" 
                label="Search Accounts" 
                onchange={handleSearch}
                placeholder="Type to search...">
            </lightning-input>

            <template if:true={searchResults.length}>
                <div class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid" style="position:relative;">
                    <ul class="slds-listbox slds-listbox_vertical" role="presentation">
                        <template for:each={searchResults} for:item="record">
                            <li key={record.Id} class="slds-listbox__item" onclick={handleRecordSelection} data-record-id={record.Id}>
                                <div class="slds-media slds-listbox__option slds-listbox__option_plain slds-media_center" role="option">
                                    <span class="slds-media__body">
                                        <span class="slds-truncate" title={record.Name}>{record.Name}</span>
                                    </span>
                                </div>
                            </li>
                        </template>
                    </ul>
                </div>
            </template>
        </div>
    </template>
</template>

Step 4: Handle Selection and Display Options

Ensure your component cleanly updates state when items are added or removed from the selected list.

360 Summary Card: Multi-Select Lookup Benefits
  • Improved UX: Replaces bulky multi-select picklists with modern search inputs.
  • Reusable Architecture: Can easily be adapted for Accounts, Contacts, or Custom Objects.
  • Lightweight: Uses native Lightning Design System (SLDS) styling without external libraries.

Summary

By pairing Apex search logic with LWC reactivity, you can build seamless multi-select lookup inputs tailored to your specific org requirements. Customize the SOQL query and template pills to adapt this pattern for any standard or custom object.