Salesforce LWC & Apex Interview Preparation Series 1 – Crack Top MNCs Like Accenture, Deloitte, TCS, Infosys

Are you preparing for a Salesforce Lightning Web Components (LWC) and Apex Developer interview at top MNCs like Accenture, Persistent Systems, TCS, KPMG, Infosys, or Deloitte?

This 3-part interview series will help you master LWC and Apex concepts with fresh, tricky, and real-world questions that test your problem-solving skills, depth of knowledge, and ability to handle complex scenarios.

This series covers:

  • Beginner to Advanced LWC Concepts
  • Apex Integration & Best Practices
  • Performance Optimization & Debugging
  • Real-World Scenarios & Tricky Questions

 Message to Candidates !

Preparing for a Salesforce developer interview—especially one centered around Lightning Web Components (LWC) and Apex—requires more than just theoretical knowledge. Employers are looking for candidates who understand how to apply concepts in real-world scenarios.

This blog is designed to help you strengthen your practical understanding through scenario-based questions that reflect the types of challenges you'll encounter during interviews. You'll explore key Apex topics such as trigger contexts, bulk processing, and best practices like recursion prevention and handling related records efficiently. On the LWC side, we cover essentials including component lifecycle, reactive properties, data flow between components, and integration with Apex.

By working through these examples, you’ll not only reinforce your technical skills but also gain the confidence to tackle complex interview questions with clarity and precision.


 About This Blog Series

In this blog series, I’ve focused on covering scenario-based Lightning Web Components (LWC) and Apex interview questions that Salesforce developers often face during technical interviews. These scenarios are crafted to mirror real-world challenges, helping you understand not just the how, but the why behind each solution.

The questions are presented in a conversational format between an interviewer and a candidate, making it easier to follow the logic, expectations, and best practices in both LWC and Apex development. From component communication and data handling in LWC to trigger design patterns and governor limit management in Apex, this series aims to sharpen your practical understanding.

Let’s begin the interview series on LWC and Apex Scenario-Based Questions and get you one step closer to acing your next Salesforce developer interview.



1. LWC Lifecycle Hooks – Real-time Use Cases

 Interviewer: Explain the full LWC lifecycle with real-time use of connectedCallback and renderedCallback.


 Interviewee: The LWC lifecycle starts with constructor(), followed by connectedCallback() when the component enters the DOM. renderedCallback() runs after the template is rendered, and can run multiple times. These hooks are useful for initializing data, interacting with the DOM, or cleaning up resources.

LWC provides hooks to handle various lifecycle events:

  • constructor(): Initializes component (no DOM access yet).
  • connectedCallback(): Called when the component is inserted into the DOM (good for data fetching).
  • renderedCallback(): Invoked after every render (DOM is ready).
  • disconnectedCallback(): Called when component is removed.

Real-Time Use of connectedCallback()


connectedCallback() {
    console.log('Component added to DOM');

    // Example: Fetching data on load
    fetchDataFromApex()
        .then(data => {
            this.records = data;
        })
        .catch(error => {
            this.error = error;
        });

    // Example: Adding event listener (e.g., for pub-sub)
    registerListener('refreshData', this.handleRefresh, this);
}

Real-Time Use of renderedCallback()


renderedCallback() {
    console.log('Component rendered or re-rendered');

    // Example: Focus an input after it's rendered
    const input = this.template.querySelector('lightning-input');
    if (input && !this.hasFocused) {
        input.focus();
        this.hasFocused = true; // prevent repeated focus on re-renders
    }
}

 MIND IT !

Full Lifecycle Flow in Practice

  1. constructor() — Initialize state
  2. connectedCallback() — Fetch initial data, subscribe to events
  3. renderedCallback() — DOM ready: update styles, focus elements
  4. (Re-rendered) — Happens on property changes ? renderedCallback() runs again
  5. disconnectedCallback() — Cleanup listeners or timers when component is removed

Best Practices

  • Avoid calling querySelector() in constructor() — the DOM is not yet available.
  • Use connectedCallback() for one-time setup and server calls.
  • Use flags (this.hasRendered) in renderedCallback() to prevent repeated logic execution.

LWC Lifecycle Diagram

LWC Lifecycle Diagram

Real-World Example: Fetching User Profile Data on Mount


// userProfile.js
import { LightningElement, wire } from 'lwc';
import getUserProfile from '@salesforce/apex/UserController.getUserProfile';

export default class UserProfile extends LightningElement {
    userData;

    connectedCallback() {
        console.log('Fetching data in connectedCallback');
        getUserProfile()
            .then(result => {
                this.userData = result;
            })
            .catch(error => {
                console.error('Error:', error);
            });
    }

    renderedCallback() {
        console.log('Component has rendered with DOM ready');
    }
}


2. LWC Architecture & Rendering Concepts

 Interviewer:What is the Shadow DOM in LWC? How does it differ from the Virtual DOM in React?


 Interviewee: The Shadow DOM is a web standard used to encapsulate a component's internal DOM structure, style, and behavior from the main document DOM. In Lightning Web Components (LWC), the Shadow DOM is used to achieve this encapsulation and maintain a clean separation between components.

In simple way I can say

  • Shadow DOM is a browser technology that encapsulates styling and markup within a component, preventing CSS/JS leakage.
  • Virtual DOM is a lightweight copy of the real DOM used by React for efficient re-rendering.
  • Key Difference: Shadow DOM is about encapsulation, while Virtual DOM is about performance optimization.

Key Points about Shadow DOM in LWC:

  • Encapsulation: Styles and markup inside the Shadow DOM don’t leak out, and external styles don’t affect it.
  • Scoped CSS: Styles defined in a component’s .css file apply only to that component.
  • DOM Isolation: Developers cannot directly access the internals of a component using standard DOM traversal methods.


3. LWC Component Communication & Data Binding

 Interviewer: How do you pass data from a Parent LWC to a Child LWC? Explain with a code example.


 Interviewee: Passing data from a Parent LWC to a Child LWC is a core concept in component communication in Salesforce. This is done using public properties (annotated with @api) in the child component.

Code Example:

1. Child Component

Child Component (childComponent.js)


// childComponent.js
import { LightningElement, api } from 'lwc';

export default class ChildComponent extends LightningElement {
    @api message; // Public property to receive data from parent
}

Child Component Template (childComponent.html)


<template>
    <p>Message from Parent: {message}</p>
</template>

2. Parent Component

Parent Component (parentComponent.js)


// parentComponent.js
import { LightningElement } from 'lwc';

export default class ParentComponent extends LightningElement {
    parentMessage = 'Hello from the Parent!';
}

Parent Component Template (parentComponent.html)


<template>
    <c-child-component message={parentMessage}></c-child-component>
</template>

Output:

Parent-to-Child Communication



4. LWC DOM Manipulation & Lifecycle Timing

 Interviewer: Why does this.template.querySelector() sometimes return null in LWC?


 Interviewee: this.template.querySelector() returns null if the element doesn’t exist in the current DOM, is conditionally rendered and not yet in the DOM, or if the method is called too early—like in the constructor. The safest place to use it is inside renderedCallback(), after the component has been rendered to the DOM.


MIND IT!

this.template.querySelector() can return null in LWC for a few key reasons:

1. Element Doesn’t Exist in the Template

If the selector doesn’t match any element in the component’s template, querySelector() will return null.

Example:


this.template.querySelector('.nonexistent'); // returns null

2. Element Is Inside a <template if:true> or <template for:each> Block and Not Rendered Yet

Conditional or looped elements are not part of the DOM until the condition is true or the list has items.

Solution: Make sure the condition is met or data is available before calling querySelector.


3. querySelector() Is Called Too Early (e.g., in constructor)

You can only use this.template.querySelector() after the template has been rendered, which means the earliest safe place to use it is in the renderedCallback() lifecycle hook.

Incorrect usage:


constructor() {
    super();
    this.template.querySelector('.my-button'); // returns null
}

Correct usage:


renderedCallback() {
    const btn = this.template.querySelector('.my-button'); // safe
}

4. Scoped to Template Only

this.template.querySelector() only looks within the component's own template Shadow DOM. It cannot access child components' internals.



5. LWC Data Binding & Apex Integration

 Interviewer: What is the purpose of @wire in LWC? How is it different from imperative Apex calls?


 Interviewee: The @wire decorator in Lightning Web Components is used to read data reactively from a Salesforce data source, such as Apex methods or Lightning Data Service (LDS), and bind it directly to the component’s properties or functions.

Example: Wire an Apex method


import { LightningElement, wire } from 'lwc';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';

export default class AccountList extends LightningElement {
    @wire(getAccounts) accounts;
}

In simple way..

  • @wire is reactive and automatically refreshes when data changes.
  • Imperative Apex requires explicit calls (promise.then() or async/await).

Example:


// Wire Method  
import { getRecord } from 'lightning/uiRecordApi';  
@wire(getRecord, { recordId: '$recordId', fields: ['Account.Name'] })  
accountData;  

// Imperative  
import getAccount from '@salesforce/apex/AccountController.getAccount';  
async loadAccount() {  
    this.account = await getAccount({ accId: this.recordId });  
}  

When to use which?

  • Use @wire when you want reactive, automatic data loading.
  • Use imperative calls when:
    • You need to fetch data on user action (e.g., button click).
    • You need more control over when/how the call is made.
    • You need to handle parameters dynamically or conditionally.

Imperative Apex Example:


import getAccounts from '@salesforce/apex/AccountController.getAccounts';

handleClick() {
    getAccounts()
        .then(result => {
            this.accounts = result;
        })
        .catch(error => {
            this.error = error;
        });
}

MIND IT !
@wire is used for declarative, reactive data fetching in LWC and is best for automatic data loading tied to component lifecycle. Imperative Apex calls are used when you need more control—like making a call on a button click or with conditional logic.



6. LWC Error Handling & Apex Integration

 Interviewer: How do you handle errors in LWC when calling Apex methods?


 Interviewee: In LWC, errors from Apex methods are handled differently based on whether the call is imperative or via @wire. Imperative calls use .catch() or try/catch, while @wire uses the error property in the response object. It's best practice to show user-friendly messages and log technical errors for support.


 MIND IT !

Error handling in LWC depends on how you're calling the Apex method — either imperatively or using the @wire decorator.

1. Imperative Apex Call — Use .catch() or try/catch

When calling Apex imperatively (e.g., inside a click handler), errors are handled using .catch() in a promise chain or try/catch in an async function.

Example:


import getAccounts from '@salesforce/apex/AccountController.getAccounts';

handleClick() {
    getAccounts()
        .then(result => {
            this.accounts = result;
        })
        .catch(error => {
            this.error = error;
            // You can also display a toast or log the error
            console.error('Error fetching accounts', error);
        });
}

With async/await:


async handleClick() {
    try {
        const result = await getAccounts();
        this.accounts = result;
    } catch (error) {
        this.error = error;
        console.error('Caught error:', error);
    }
}

2. @wire Apex Call — Error Object

When using @wire, you handle errors through the response object’s error property.

Example:

@wire(getAccounts)
wiredAccounts({ error, data }) {
    if (data) {
        this.accounts = data;
    } else if (error) {
        this.error = error;
        console.error('Error in @wire:', error);
    }
}

Common Error Sources in Apex Calls:

  • Unhandled exceptions in the Apex method (e.g., null pointer, DML exception).
  • SOQL queries returning no rows (with Database.getQueryLocator or List operations).
  • Missing object or field-level permissions.
  • Governor limits exceeded.

Best Practices for Handling Errors in LWC:

  • Always provide user-friendly feedback (e.g., Lightning toast messages).
  • Log technical errors for debugging.
  • Use try/catch in Apex code too and return meaningful error messages to LWC.
  • Use ShowToastEvent to show errors in the UI.

Toast Example:


import { ShowToastEvent } from 'lightning/platformShowToastEvent';

this.dispatchEvent(new ShowToastEvent({
    title: 'Error',
    message: error.body.message,
    variant: 'error'
}));


7. Sibling Component Communication

 Interviewer: How Do You Share Data Between Unrelated LWC Components?


Scenario:
Two sibling components (not parent-child) need to sync data (e.g., a filter component and a datatable).


 Interviewee: 


  1. Use Lightning Message Service (LMS) (for same page).
  2. Use pubsub library (event-based).

Example (LMS):


// Publisher Component
import { LightningElement } from 'lwc';
import { publish, MessageContext } from 'lightning/messageService';
import FILTER_UPDATE from '@salesforce/messageChannel/FilterUpdate__c';

export default class FilterComponent extends LightningElement {
    handleFilterChange(event) {
        const filterValue = event.detail.value;
        publish(this.messageContext, FILTER_UPDATE, { filter: filterValue });
    }
}

// Subscriber Component
import { LightningElement, wire } from 'lwc';
import { subscribe, MessageContext } from 'lightning/messageService';
import FILTER_UPDATE from '@salesforce/messageChannel/FilterUpdate__c';

export default class DataTableComponent extends LightningElement {
    @wire(MessageContext) messageContext;

    connectedCallback() {
        this.subscription = subscribe(
            this.messageContext,
            FILTER_UPDATE,
            (message) => this.handleFilterUpdate(message)
        );
    }

    handleFilterUpdate(message) {
        this.filterValue = message.filter;
    }

Why Interviewers Ask This:

  • Tests knowledge of cross-component communication.
  • Common in dashboards and complex UIs.


8. Role-Based Visibility in LWC Components

 Interviewer: How do you implement role-based visibility in reusable LWC components?


 Interviewee: You can use Apex to fetch the user's profile or permission set and conditionally render parts of your UI.

Example:


// roleBasedComponent.js
import { LightningElement, track } from 'lwc';
import getUserRole from '@salesforce/apex/UserAccessController.getUserRole';

export default class RoleBasedComponent extends LightningElement {
    @track isManager = false;

    connectedCallback() {
        getUserRole()
            .then(role => {
                this.isManager = (role === 'Manager');
            });
    }
}



<template>
    <template if:true={isManager}>
        <p>You have manager access!</p>
    </template>
    <template if:false={isManager}>
        <p>Standard view only</p>
    </template>
</template>

Apex Class: UserAccessController


public with sharing class UserAccessController {
    
    @AuraEnabled(cacheable=true)
    public static String getUserRole() {
        // Get current user's Id
        Id userId = UserInfo.getUserId();

        // Query the user's Role
        User usr = [
            SELECT UserRole.Name 
            FROM User 
            WHERE Id = :userId 
            LIMIT 1
        ];

        // Return role name (may return null if user has no role)
        return usr.UserRole != null ? usr.UserRole.Name : 'No Role';
    }
}

Output:

Role-Based Visibility



9. Caching Techniques for Performance

 Interviewer: What caching techniques do you use in LWC?


 Interviewee: In LWC, I use Lightning Data Service for automatic caching, JavaScript variables or Maps for in-memory caching, and sessionStorage when I need persistence across components. For Apex data, I ensure proper handling on the client to avoid repeated calls. These approaches help improve performance and user experience.

Common Caching Techniques in LWC

1. Lightning Data Service (LDS) Caching

  • LDS automatically caches records in the client-side cache.
  • When using lightning-record-form, lightning-record-view-form, or lightning-record-edit-form, Salesforce does not refetch the data if it’s already cached.
  • Use getRecord or getRecordCreateDefaults with @wire to benefit from this automatic caching.


import { getRecord } from 'lightning/uiRecordApi';

@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
record;

Benefit: No Apex needed, automatically manages data consistency and cache.

2. Client-Side JavaScript Caching (Manual)

  • You can store fetched data in a component-level variable or Map to prevent repeated calls.


let cache = new Map();

if (cache.has(recordId)) {
    this.data = cache.get(recordId);
} else {
    fetchRecord(recordId).then(result => {
        cache.set(recordId, result);
        this.data = result;
    });
}

Use this for frequently accessed but rarely changing data during a component's session.

3. Session Storage / Local Storage

  • For cross-component or session-persistent caching.
  • Use sessionStorage or localStorage (browser APIs).


const cached = sessionStorage.getItem('accountData');
if (cached) {
    this.accounts = JSON.parse(cached);
} else {
    fetchAccounts().then(data => {
        sessionStorage.setItem('accountData', JSON.stringify(data));
        this.accounts = data;
    });
}

Be cautious with sensitive data. Prefer sessionStorage over localStorage for security.

4. Storable Action (for Apex)

  • If using @salesforce/apex and caching is critical, use Platform Cache in Apex or storable actions (in Aura only — not directly supported in LWC).
  • In LWC, you must implement client-side caching manually, but Apex can use platform-level caching.


10. Server-Side Pagination with Debounced Search

 Interviewer: How do you implement search with server-side debouncing and pagination?


 Interviewee: Use setTimeout for debouncing and call Apex with offset and limit for pagination.


Code Snippet:


// searchableTable.js
import { LightningElement, track } from 'lwc';
import searchRecords from '@salesforce/apex/SearchController.searchRecords';

let timeout;

export default class SearchableTable extends LightningElement {
    @track searchKey = '';
    @track data = [];
    @track page = 1;

    handleSearchChange(event) {
        clearTimeout(timeout);
        this.searchKey = event.target.value;

        timeout = setTimeout(() => {
            this.searchData();
        }, 300); // debounce 300ms
    }

    searchData() {
        searchRecords({ keyword: this.searchKey, pageNumber: this.page })
            .then(result => {
                this.data = result;
            });
    }
}


11. LWC Performance Optimization & Data Handling

 Interviewer: How do you handle large data sets in LWC without performance issues?


 Interviewee: 


  • Use Pagination (LIMIT and OFFSET in SOQL).
  • Implement Infinite Scrolling with lightning-datatable.


12. LWC Reactive System & State Management

 Interviewer: Why does @track no longer need explicit declaration in modern LWC?


Scenario:
A developer sees that removing @track still makes properties reactive. Why?


 Interviewee: Since Spring '20, LWC automatically tracks primitive values and object/array mutations (thanks to Proxies).

Still Needed?

  • Only needed if reassigning entire objects/arrays (e.g., this.list = newList).


13. LWC Event Handling & Component Communication

 Interviewer: How do you prevent event bubbling in LWC?


Scenario:
A click event in a child component triggers a parent’s click handler unintentionally.


 Interviewee: Use event.stopPropagation().


Child Component (JS):


handleClick(event) {  
    event.stopPropagation(); // Prevents bubbling  
    // Handle child click  
} 


14. LWC Asynchronous JavaScript & Apex Integration

 Interviewer: How do you handle multiple Apex calls in parallel and wait for all to finish?


Scenario:
A dashboard needs data from 3 different Apex methods before rendering.

  • getAccountStats()
  • getOpportunityStats()
  • getCaseStats()

 Interviewee: In this case, you'll use Promise.all() to call all Apex methods in parallel and wait until all of them return results before rendering the dashboard.

Example:


import { LightningElement } from 'lwc';
import getAccountStats from '@salesforce/apex/DashboardController.getAccountStats';
import getOpportunityStats from '@salesforce/apex/DashboardController.getOpportunityStats';
import getCaseStats from '@salesforce/apex/DashboardController.getCaseStats';

export default class DashboardComponent extends LightningElement {
    accountData;
    opportunityData;
    caseData;
    isLoading = true;
    error;

    connectedCallback() {
        this.loadDashboardData();
    }

    async loadDashboardData() {
        this.isLoading = true;

        try {
            const [accounts, opportunities, cases] = await Promise.all([
                getAccountStats(),
                getOpportunityStats(),
                getCaseStats()
            ]);

            this.accountData = accounts;
            this.opportunityData = opportunities;
            this.caseData = cases;
        } catch (err) {
            this.error = err;
            console.error('Error loading dashboard data:', err);
        } finally {
            this.isLoading = false;
        }
    }
}

Why Use Promise.all()?

  • All 3 calls are independent — there’s no dependency between them.
  • Promise.all() runs them in parallel, improving performance.
  • It waits until all promises are resolved before proceeding.
  • If any of them fail, it enters the catch block.

MIND IT !
Using Promise.all() lets me make multiple independent Apex calls concurrently and wait for all to finish before rendering the component. This ensures optimal performance and better user experience.



 MIND IT !

Facing interview is very stressful situation for everyone who want to get the job. For every problem there is a solution. Practice the best solution to crack the interview. Pick the best source and practice your technical and HR interview with experienced persons which helpful to boost confidence in real interview.

Share This Post:

About The Author

Hey, my name is Saurabh Samir, and I am a Salesforce Developer with a passion for helping you elevate your knowledge in Salesforce, Lightning Web Components (LWC), Salesforce triggers, and Apex. I aim to simplify complex concepts and share valuable insights to enhance your Salesforce journey. Do comment below if you have any questions or feedback—I'd love to hear from you!