Skip to content
Salesforce Interview Q&A 20 min read 7 sections

Salesforce LWC Interview Questions and Answers

The front-end round usually comes after the Apex round, and it is taken by whoever owns the UI layer on the project. The questions stay close to what you touch every day: how a component renders, which decorator does what, how two components talk when they sit in different places on a record page, a

RL

RizeX Labs

RizeX Labs

Published

Last updated

Related programme

Salesforce Training

Admin, Apex, Lightning Web Components and integrations in a live batch, with an industry project, mock interviews and a network of 270 hiring companies.

See the curriculum

3000+ learners · 270+ hiring partners

The front-end round usually comes after the Apex round, and it is taken by whoever owns the UI layer on the project. The questions stay close to what you touch every day: how a component renders, which decorator does what, how two components talk when they sit in different places on a record page, and how data reaches the browser without breaking sharing rules. This post covers 30 Lightning Web Component questions asked in real interview rounds, grouped into five areas. Each answer gives the definition first, then the reason it matters in a build, then code where code explains it faster than prose.

Aura versus LWC and the rendering model

Difference between Aura and LWC

Aura is Salesforce's own component framework from 2014. LWC is built on modern web standards (ES6+ modules, custom elements, shadow DOM, template syntax compiled to JavaScript) with a thin Salesforce layer on top.

Point Aura LWC
Base Proprietary framework Web standards plus a small Salesforce layer
Files .cmp, controller.js, helper.js, renderer.js .html, .js, .js-meta.xml
Rendering Framework-managed Virtual DOM with a reactivity system
Value binding Two-way in many cases One-way down, events up
Performance Heavier, more framework code shipped Lighter, browser does most of the work
Security Locker Service Lightning Web Security

Both can live on the same page. An Aura component can contain an LWC, but an LWC cannot contain an Aura component, and that single rule sets migration order: convert leaf components first and work upward.

What is Shadow DOM?

Shadow DOM is the browser feature that gives each component its own DOM subtree. Markup and styles inside a component are isolated from the page. A parent cannot reach into a child's internals with this.template.querySelector, and page-level CSS does not restyle the component's inside.

Salesforce shipped a synthetic shadow DOM polyfill for older browser support, and newer components can run in native shadow. Either way, query only your own markup and never write a selector that assumes the DOM of another component.

What is Virtual DOM?

The HTML template compiles into a JavaScript function that produces a virtual representation of the DOM. When a reactive field changes, the engine builds the new tree, compares it with the previous one, and patches only the nodes that differ.

Two consequences matter in code review. Put a unique key on every item inside for:each so the diff can match items across renders, never the list index. And keep template getters cheap, because they run on every render cycle.

How do you handle CSS scoping?

A component's .css file is scoped to that component. Styles do not leak out and page styles do not leak in.

  • :host targets the component's own host element, for example to set display or width.
  • Shared styles go in a CSS-only module and are pulled in with @import 'c/cssLibrary';.
  • To let a parent theme a child, expose CSS custom properties or use the SLDS styling hooks, because custom properties cross the shadow boundary while plain selectors do not.

What is Base Lightning Components?

Base components are the ready-made components in the lightning namespace: lightning-input, lightning-datatable, lightning-record-form, lightning-card and the rest. Salesforce styles them to SLDS, keeps them accessible, and upgrades them each release. Use them before writing your own markup. Rebuilding a data table with raw <table> tags when lightning-datatable already handles sorting, row actions and infinite loading is a signal that you have not worked on a real backlog.

How do you build reusable components?

A component becomes reusable when it knows nothing about its parent.

  • Take everything it needs through @api properties. No hard-coded record Ids, no hard-coded field API names.
  • Report outcomes with a CustomEvent, and use <slot> for content the parent should control.
  • Keep Apex out of it where you can. A component that receives data is reusable across objects. The wrapper that fetches the data is not.

Memorised definitions collapse the moment someone asks for a real project example. Be ready to name the component you made reusable, the two places it is used, and the property you had to add for the second use.

Decorators and lifecycle

Explain LWC lifecycle hooks

The hooks, in the order they fire when a component is created:

  1. constructor() fires first. The element is not yet in the DOM, this.template.querySelector returns nothing, and public properties are not set yet. Call super() as the first statement.
  2. connectedCallback() fires when the element is inserted into the DOM. Public properties are available. Good place to subscribe to a message channel or start an imperative Apex call.
  3. render() is optional. It returns which template to use when a component has more than one.
  4. renderedCallback() fires after rendering completes. It fires on every re-render, so guard one-time work with a boolean flag.
  5. disconnectedCallback() fires when the element is removed from the DOM. Unsubscribe here.
  6. errorCallback(error, stack) fires when a descendant component throws.

Parent and child order matters, and this is where interviewers separate people. On insertion the parent's constructor, connectedCallback and render run first, then the child's constructor, connectedCallback, render and renderedCallback, and only then the parent's renderedCallback. So the child finishes rendering before the parent does. On removal the direction reverses: disconnectedCallback fires on the parent first, then on the children.

What is @api decorator?

@api marks a property or a method as public. A public property is set by the parent in markup and is reactive, so the child re-renders when the parent changes it. A public method is called by the parent from JavaScript.

The rule people break: a child must never reassign its own @api property. Data flows down, changes flow back up as events. If the child needs to edit the value, copy it into a private field and emit the edit. Property names are camelCase in JavaScript and kebab-case in markup, so recordId becomes record-id.

What is @track?

Before Spring '20, @track was needed on any field you wanted the template to react to. Since Spring '20, all fields are reactive by default. If the value changes, the component re-renders.

@track is still needed in one situation: when you mutate the internals of an object or an array in place rather than reassigning it. Pushing into an array or changing one key of an object does not trigger a re-render unless the field is tracked.

export default class Example extends LightningElement {
    filters = { status: 'New' };          // reassigning works without @track
    @track rows = [];                     // tracked because we mutate in place

    addRow(row) {
        this.rows.push(row);              // in-place mutation, needs @track
    }

    changeStatus(value) {
        this.filters = { ...this.filters, status: value };  // new object, reactive anyway
    }
}

Most teams now avoid @track and reassign with the spread syntax instead.

What is @wire?

@wire connects a property or a function to a wire adapter, which is a data provider such as an Apex method marked cacheable or an adapter from lightning/uiRecordApi. The framework calls the adapter, provisions the result, and re-provisions it whenever a reactive parameter changes.

A parameter becomes reactive when it is prefixed with $, for example $recordId, and while that parameter is undefined the adapter does not call the server. Wired to a property you get { data, error }. Wired to a function you receive the same object and can transform it before rendering.

How do you handle error boundaries?

errorCallback(error, stack) in a parent catches errors thrown in the lifecycle hooks and render of its descendants. Set a flag in it and render a fallback so one broken child does not blank the page.

It does not catch everything. Errors in the component's own hooks, in event handlers and inside asynchronous callbacks such as a rejected Apex promise are not caught there. Those need try/catch and .catch() on the promise.

errorCallback(error, stack) {
    this.hasError = true;
    this.message = error?.body?.message ?? error?.message ?? 'Unexpected error';
    console.error(stack);
}

Component communication

How do you communicate parent to child?

Two ways, both through @api. Set a public property in markup, or call a public method on the child element.

// child: contactCard.js
import { LightningElement, api } from 'lwc';

export default class ContactCard extends LightningElement {
    @api recordId;      // set by the parent in markup
    @api title = 'Contact';
    isOpen = false;

    @api
    expand() {          // called by the parent through querySelector
        this.isOpen = true;
    }
}
<!-- parent: accountPanel.html -->
<template>
    <c-contact-card record-id={selectedId} title="Primary contact"></c-contact-card>
    <lightning-button label="Expand" onclick={handleExpand}></lightning-button>
</template>
// parent: accountPanel.js
handleExpand() {
    this.template.querySelector('c-contact-card').expand();
}

Setting the property is the preferred route. Call a public method only for actions with no state to bind, such as focus or reset.

How do you communicate child to parent?

The child dispatches a CustomEvent and the parent listens with an on handler in markup.

// child: rowItem.js
handleSelect() {
    this.dispatchEvent(new CustomEvent('rowselect', {
        detail: { recordId: this.recordId, action: 'select' }
    }));
}
<!-- parent -->
<c-row-item record-id={row.Id} onrowselect={handleRowSelect}></c-row-item>
// parent
handleRowSelect(event) {
    this.selectedId = event.detail.recordId;
}

Event names are lowercase with no hyphens and no camelCase, because the handler attribute is on plus the name. Send primitive values in detail where you can, since the receiver gets a reference to whatever object you pass.

What is Custom Event?

CustomEvent is the standard DOM event constructor LWC uses for component communication. The second argument carries the configuration:

  • detail holds the payload.
  • bubbles: true lets the event travel up the DOM tree.
  • composed: true lets it cross the shadow boundary.

Default is bubbles: false, composed: false, so the event is heard only by the immediate parent. Keep that default. composed: true makes the event audible far outside the component and turns an internal detail into a contract you cannot change later.

How do you handle events in LWC?

Down the tree, pass properties. Up the tree, dispatch events. Across the tree, where there is no parent and child relationship, use the Lightning Message Service.

Handlers are attached declaratively in the template (onclick, onchange, onrowselect). If you attach a listener manually to window, remove it in disconnectedCallback, otherwise you leak a listener every time the component is re-created.

What is pub-sub model?

The pub-sub pattern is the older way to let two sibling components talk. A small pubsub JavaScript module keeps a registry of subscribers, one component publishes an event name with a payload, and subscribed components receive it.

Its limitation is why it faded. The utility works only within a single Lightning page and does not cross an Aura or Visualforce boundary. Older orgs still carry it, but on a new build the answer is the Lightning Message Service.

How do you use Lightning Message Service?

LMS is the supported publish and subscribe mechanism across the whole page, including between LWC, Aura and Visualforce.

  1. Create a message channel as metadata, SampleChannel__c.messageChannel-meta.xml, with isExposed and the fields you will send.
  2. Import the channel from @salesforce/messageChannel/SampleChannel__c and the functions publish, subscribe, unsubscribe, MessageContext and APPLICATION_SCOPE from lightning/messageService.
  3. Wire the context with @wire(MessageContext) messageContext;
  4. Publish with publish(this.messageContext, SAMPLE, payload).
  5. Subscribe in connectedCallback, keep the subscription, and unsubscribe in disconnectedCallback.

By default a subscriber hears messages only from its own active area. Pass { scope: APPLICATION_SCOPE } when the subscriber sits in the utility bar or needs messages from anywhere in the application.

What is Modal pattern in LWC?

Two approaches, and either is accepted if you explain the trade-off.

The base component route uses lightning/modal. Extend LightningModal and open it with MyModal.open({ size: 'small', content: value }), which returns a promise resolving to whatever the modal closes with. Focus handling and accessibility come built in.

The manual route renders SLDS modal markup behind lwc:if and closes by dispatching an event to the parent, leaving you to handle focus trapping and the escape key. Use it only when the base component cannot produce the markup you need.

What is Toast notification?

A toast is the small message bar that appears at the top of the page. Import ShowToastEvent from lightning/platformShowToastEvent, then dispatch it with a title, message, variant (success, error, warning, info) and mode (dismissable, pester, sticky).

The container displays the toast, not your component, so it works in Lightning Experience and Experience Cloud but not in every host. Where the host does not support it, fall back to an inline message.

Data access

Wire vs Imperative Apex calls

This is the question where the trainer's warning shows up most: candidates know the syntax of both and cannot say when each one runs. Answer the timing first.

Point @wire Imperative
When it runs Automatically when the component is created, and again whenever a reactive $ parameter changes Only when your code calls it, for example in a click handler
Apex requirement Method must be @AuraEnabled(cacheable=true) Works with any @AuraEnabled method
DML Not allowed, the method is cacheable Allowed
Result Provisioned as { data, error } to a property or function, and can be provisioned more than once Returns a promise you resolve with then/catch or await
Caching Result is held in the client cache and can be refreshed with refreshApex No framework cache
Control You cannot decide when it fires You control the order and can chain calls

Two details people miss. The wire request is set up when the component is created, so data can arrive before or after connectedCallback finishes. Never assume wired data is available inside connectedCallback. And cacheable=true is a promise you make to the platform that the method does not change data. Adding DML to a cacheable method throws at runtime.

Use @wire for read-only data that depends on a record Id or a filter. Use imperative for saves, for anything triggered by a button, and for calls where sequence matters.

import { LightningElement, api, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
import saveContact from '@salesforce/apex/ContactController.saveContact';
import { refreshApex } from '@salesforce/apex';

export default class ContactList extends LightningElement {
    @api recordId;
    contacts;
    error;
    wiredResult;

    // Apex: @AuraEnabled(cacheable=true) public static List<Contact> getContacts(Id accountId)
    // Runs on creation and again every time recordId changes.
    @wire(getContacts, { accountId: '$recordId' })
    wiredContacts(result) {
        this.wiredResult = result;
        if (result.data) {
            this.contacts = result.data;
            this.error = undefined;
        } else if (result.error) {
            this.error = result.error;
            this.contacts = undefined;
        }
    }

    // Apex: @AuraEnabled public static Id saveContact(Contact record)  -- not cacheable, it does DML
    async handleSave(event) {
        try {
            await saveContact({ record: event.detail.record });
            await refreshApex(this.wiredResult);   // refresh the cached wire result
        } catch (error) {
            this.error = error;
        }
    }
}

What is Lightning Data Service?

LDS is the platform's own data layer for records. It gives you wire adapters such as getRecord and getRecordUi, functions such as createRecord, updateRecord and deleteRecord from lightning/uiRecordApi, and the base components lightning-record-form, lightning-record-edit-form and lightning-record-view-form.

What you gain by using it instead of Apex:

  • No Apex class and no Apex test class for simple record work.
  • Field-level security and sharing are applied by the platform.
  • A shared client cache, so two components asking for the same record cause one server call, and an update in one component refreshes the others.

Its limit is scope. LDS works one record at a time. For a filtered list, an aggregate or a multi-object save, you go back to Apex.

How do you handle pagination?

Three patterns, chosen by data volume.

  • Client-side slicing. Load a bounded set once and slice it for the current page. Fine up to a few hundred rows.
  • LIMIT with OFFSET in SOQL. It supports jumping to a page number, but OFFSET is capped at 2000 rows and gets slower the deeper you go.
  • Keyset pagination. Order by a stable field, remember the last value of the previous page, and query WHERE Id > :lastId ORDER BY Id LIMIT 50. No offset cap and flat cost, but no jumping to a page number.

For infinite scroll, lightning-datatable with enable-infinite-loading and onloadmore handles the interaction while Apex serves the next keyset page.

How do you test LWC?

Jest, through @salesforce/sfdx-lwc-jest. A test creates the element with createElement, appends it to document.body, flushes the microtask queue, then asserts against element.shadowRoot.querySelector(...).

Server calls are mocked, never made. For imperative Apex, jest.mock the @salesforce/apex/Class.method module and resolve or reject it. For a wire adapter, import the adapter in the test and call .emit(mockData), then flush promises before asserting. Keep sample payloads as JSON under __tests__/data.

Assert on rendered output and on dispatched events, not on private variables.

Performance and security

How do you optimize LWC performance?

  • Reduce server round trips. Use cacheable Apex or LDS so repeat reads come from the client cache, and combine several small calls into one wrapper response.
  • Return only the fields you need. A wide SOQL over 50 fields for a table showing 5 of them costs on both ends.
  • Give every for:each a stable unique key so the diff patches instead of rebuilding.
  • Compute once when data arrives and store the result, instead of doing the work in a template getter that runs on every render.
  • Guard renderedCallback with a flag, and debounce keystroke handlers before they reach the server.

What is Lazy loading?

Lazy loading means not paying for something until it is needed. Content behind lwc:if is not constructed until the condition turns true. Data for a tab is fetched when the tab opens rather than on page load. Third-party libraries load with loadScript and loadStyle from lightning/platformResourceLoader at the point of use.

renderedCallback() {
    if (this.chartInitialized) {
        return;
    }
    this.chartInitialized = true;
    loadScript(this, CHART_JS)
        .then(() => this.renderChart())
        .catch(error => this.handleError(error));
}

What is Dynamic component loading?

Dynamic loading means deciding at runtime which component to render, instead of hard-coding the tag in the template. You resolve the constructor with a dynamic import() and render it through lwc:component with lwc:is.

async connectedCallback() {
    const { default: ctor } = await import(`c/${this.componentName}`);
    this.componentConstructor = ctor;
}
<template>
    <lwc:component lwc:is={componentConstructor}></lwc:component>
</template>

It suits a form that renders a different editor per record type, or a wizard whose steps are configured in metadata. The module loads over the network at that moment, so keep the set of possible components small and handle the failure case.

How do you secure LWC?

Security in LWC sits in layers, and an interviewer wants you to name more than one.

  • Lightning Web Security sandboxes your JavaScript and isolates it from other namespaces.
  • The template escapes interpolated values, so {value} cannot inject markup. Bypassing it with lwc:dom="manual" and innerHTML puts the escaping back on you.
  • Apex is not secure by default and cacheable=true enforces nothing. Query in user mode with WITH USER_MODE, or apply Security.stripInaccessible, or check isAccessible and isUpdateable. LDS applies FLS for you, Apex does not.
  • Content Security Policy blocks external scripts unless the origin is a registered CSP Trusted Site, and blocks inline script and eval outright.
  • Never put a secret in JavaScript. Named Credentials keep it server-side.

What is Locker Service?

Locker Service is the older client-side security architecture. It wrapped the DOM and browser globals in secure proxies, gave each namespace its own restricted window and document, and blocked direct access to other components' internals.

It did its job but it was strict about the JavaScript it allowed, and several third-party libraries would not run under it because the proxied objects did not behave like the native ones. Locker Service continues to apply to Aura components.

What is Lightning Web Security?

Lightning Web Security is the architecture that replaces Locker Service for Lightning web components. Instead of wrapping objects in proxies, it gives each namespace its own JavaScript sandbox with its own copy of the browser globals, and it controls what crosses between sandboxes.

What changes in practice:

  • More standard JavaScript is allowed, so libraries that failed under Locker Service usually run.
  • Access to native browser APIs is broader, with the risky ones filtered rather than blocked wholesale.
  • It applies to Lightning web components only. Aura components stay on Locker Service, which is why a mixed page still has both models active.

Newer orgs have it switched on already, and older orgs enable it in Session Settings. Retest third-party libraries after switching.

What is Experience Cloud with LWC?

To use a component in an Experience Cloud site, the meta XML must expose it to the community targets, typically lightningCommunity__Page along with lightningCommunity__Default, and any properties you want a site builder to set go in targetConfigs.

The part that catches people is the guest user. A guest has its own profile and its own sharing model, so a component that works for you as an internal user can render empty in the site, and the fix is almost always permissions rather than code.

This is where the debugging question lands: "the component shows no data in the site but works in the app, what do you check?" beats "what is Experience Cloud?" every time. Answer it in order. Is the Apex class enabled on the guest user profile, does that profile have read on the object and the fields, and is there a guest user sharing rule for those records.

What the interviewer is actually checking

  • Aura versus LWC: whether you explain the rendering model rather than list file extensions, and whether you know an LWC cannot contain an Aura component.
  • Decorators and lifecycle: whether you can state the hook order including the parent and child sequence, and whether you know @track is now only for in-place mutation.
  • Communication: whether you pick the right mechanism. Properties down, events up, message service across. Reaching for LMS between a parent and child says you learned patterns without the reasons.
  • Data access: whether you can say when each call runs, not just how to write it. Wire fires on creation and on reactive parameter change and needs cacheable=true. Imperative fires when you call it and is the only route for DML.
  • Performance and security: whether you know the client layer does not enforce CRUD and FLS for Apex, and that Lightning Web Security covers LWC while Aura stays on Locker Service.

Where to go from here

Prepare these answers the way the round runs. Definition in one line, then the project example, then the follow-up that goes a level deeper. For every component on your resume, know which object it sits on, why it was built as an LWC rather than a screen flow, and what broke while you built it.

For structured practice on Apex, LWC and integrations, with mock interview rounds and project work you can defend, see the Salesforce training in Pune with placement support at RizeX Labs.

Last updated 4 October 2026