Top 40 Salesforce LWC Interview Questions & Answers 2025 — Lightning Web Components Deep Dive with Real Code Examples

📅  LWC
Salesforce LWC Interview Questions — Lightning Web Components Explained
⚡ LWC

Salesforce LWC Interview Questions — Lightning Web Components Explained

Decorators, Lifecycle Hooks, Wire, LMS, Navigation, LDS, Events & Real Code — Real Scenarios Explained for Interviews

Basic Intermediate Advanced 40 Questions
Q
Question 01 · 🟢 Basic
Why do we use LWC? We already have Aura — what's the difference and how can you tell which is faster?
✅ Answer
LWC is built on native Web Standards (HTML, ES6+ JS, CSS) — no proprietary abstraction layer. It's faster, lighter, and the future of Salesforce UI development! ⚡
📋 LWC vs Aura Comparison
FeatureLWCAura (Old)
Based onWeb Standards (HTML/JS/CSS)Proprietary Aura framework
PerformanceFast — native browser APIsSlower — heavy abstraction
Learning CurveEasy — standard JSSteeper — Aura-specific syntax
Embed Aura in LWC?❌ No✅ Can embed LWC inside Aura
Future✅ Actively developed⚠️ Legacy — no new features
📌 How to Tell Which is Faster
  • Chrome DevTools Performance tab — LWC shows fewer scripting cycles
  • Network tab — LWC JS bundle sizes are noticeably smaller
  • Re-renders on filter change are instant in LWC vs visible lag in Aura
  • On mobile (Experience Cloud), LWC is significantly smoother
🌍 Real World Example

At XYZ Company, the old Quote summary Aura component was rebuilt as LWC. The LWC version loaded in ~400ms vs ~900ms for Aura on the same machine — measurable in browser DevTools. Re-render on currency toggle was instant in LWC vs 200–300ms lag in Aura.

🎤 One-Line Answer for Interview
"LWC is faster because it compiles down to native browser APIs without Aura's proprietary engine overhead — I measured this in real projects using Chrome DevTools Performance tab and noticed smaller JS bundles and faster re-renders in LWC vs equivalent Aura components."
Q
Question 02 · 🟢 Basic
What is the purpose of the HTML file in a Lightning Web Component bundle?
✅ Answer
The HTML file defines the UI template — what the component looks like and renders. It must have a root <template> tag and uses Salesforce directives for data binding, loops, and conditionals! 🖼️
📋 What the HTML File Controls
FeatureDetails
Root tagMust be <template>
Data binding{propertyName} — one-way from JS to HTML
Conditionalslwc:if / lwc:else (new) or if:true / if:false (old)
Loopsfor:each + key, or iterator
Child components<c-child-component>
Slots<slot> — lets parent inject content
🔑 Key Points for Interviewer
  • 🔥HTML file is mandatory — every LWC must have it
  • 💡Template expressions {value} are read-only — no JS expressions like {a + b}
  • 💡Shadow DOM is applied automatically — styles are encapsulated per component
  • 💡HTML holds UI structure only — business logic belongs in the JS file
🎤 One-Line Answer for Interview
"The HTML file in LWC defines the component's UI template using standard HTML with Salesforce directives for data binding, loops, and conditional rendering — it's the view layer of the component and is mandatory in every LWC bundle."
Q
Question 03 · 🟢 Basic
What is the purpose of the JavaScript file in a Lightning Web Component bundle?
✅ Answer
The JS file is the brain of the component — it holds all logic, state, reactive properties, lifecycle hooks, event handlers, and Apex calls! 🧠
📋 What the JS File Controls
ResponsibilityDetails
Class definitionexport default class MyComp extends LightningElement
PropertiesReactive state via @track, @api
Wire adapters@wire for declarative Salesforce data
Event handlersMethods bound to HTML events
Lifecycle hooksconnectedCallback, renderedCallback, etc.
Apex callsImperative or wire-based
🔑 Key Points for Interviewer
  • 🔥Must use export default class ... extends LightningElement
  • 💡Every public method/property exposed via @api lives here
  • 💡JS file and HTML file must share the same component name
  • 💡UI layout belongs in HTML — business logic belongs here
🎤 One-Line Answer for Interview
"The JavaScript file contains the component class with all reactive properties, lifecycle hooks, event handlers, and data logic — it's the controller of the component and must export a default class extending LightningElement."
Q
Question 04 · 🟠 Intermediate
What are the types of decorators in LWC? Can we use multiple decorators on one property?
✅ Answer
LWC has 3 decorators — @api, @track, and @wire. And NO — you cannot use multiple decorators on one property. They are mutually exclusive! ❌
📋 The Three Decorators
DecoratorPurposeReactive?Public?
@apiExpose property/method publicly to parent✅ Yes✅ Yes
@trackDeep reactivity for objects and arrays✅ Yes (deep)❌ No
@wireDeclarative Salesforce data fetching✅ Reactive to $ params❌ No
// ❌ WRONG — Cannot combine decorators @api @track myProperty; @api @wire(getRecord) record; // ✅ CORRECT — One decorator per property @api recordId; @track filters = { type: '', status: '' }; @wire(getRecord, { recordId: '$recordId' }) wiredRecord;
🔑 Key Points for Interviewer
  • 🔥@api + @track = compile error. @api + @wire = compile error
  • 💡@api already makes a property reactive — @track is redundant on it
  • 💡Since Spring '20, primitives are auto-reactive — @track is only needed for nested objects/arrays
🎤 One-Line Answer for Interview
"LWC has three decorators: @api for public interface, @track for deep internal reactivity, and @wire for reactive Salesforce data — they are mutually exclusive. Combining them on one property causes a compile error."
Q
Question 05 · 🟠 Intermediate
Explain all three decorators in LWC — @api, @track, and @wire — and when to use each?
✅ Answer
@api = public interface, @track = internal deep reactivity, @wire = reactive Salesforce data fetching — three decorators, three completely different purposes! 🎯
📌 @api — Public Property or Method
@api recordId; // Parent can pass this in @api refresh() { ... } // Parent can call this method
  • Makes property accessible from parent components or App Builder
  • Any change from parent triggers child re-render
  • Child should NOT mutate @api properties — one-way flow only
📌 @track — Deep Reactivity
@track filters = { type: '', region: '' }; // Changing filters.type → re-renders ✅ (without @track it won't!)
  • Makes LWC deeply reactive — nested property changes trigger re-render
  • 💡Since Spring '20, primitives are auto-reactive — @track mainly needed for objects/arrays
  • Internal use only — parent cannot access @track properties
📌 @wire — Declarative Data Fetching
@wire(getLineItems, { quoteId: '$recordId' }) wiredItems({ data, error }) { if (data) this.lineItems = data; if (error) this.error = error; }
  • Auto-fetches Salesforce data and re-fetches when $ reactive params change
  • Returns {data, error} — always handle both cases
  • Requires cacheable=true on the Apex method — DML not allowed
🎤 One-Line Answer for Interview
"@api exposes properties/methods publicly to parent components, @track enables deep reactivity for internal objects and arrays, and @wire declaratively fetches and subscribes to Salesforce data — each serves a distinct purpose and cannot be combined on the same property."
Q
Question 06 · 🟠 Intermediate
How many times does the wire method execute? When does it re-run?
✅ Answer
Wire executes once on load and re-executes every time its reactive $ parameter changes. If the reactive parameter is undefined or null — wire does NOT fire at all! 🔁
📋 Execution Scenarios
ScenarioWire Executes
No reactive property ($)Once — on component initialization
Reactive property changes 3 times3 times — once per change
Reactive property is undefinedWire does NOT fire ❌
User navigates away and backRe-executes on re-initialization
// $recordId is reactive — wire re-runs on every change @wire(getRecord, { recordId: '$recordId', fields: FIELDS }) wiredRecord; // No $ prefix — runs once only @wire(getAllAccounts, { type: 'Customer' }) accounts;
🔑 Key Points for Interviewer
  • 🔥Wire is part of the reactive engine of LWC — you don't call it manually
  • 💡Wire fires before connectedCallback() in the lifecycle
  • 💡Wire does NOT re-execute if a non-reactive (no $) parameter changes
🎤 One-Line Answer for Interview
"Wire executes once on load and re-executes every time its reactive $ parameter changes value — if the reactive parameter is undefined, wire does not fire at all. It's managed by the framework, not called manually."
Q
Question 07 · 🟠 Intermediate
Why use wire? What is connectedCallback()? Do both call automatically? What is the difference?
✅ Answer
✅ Both are automatic — but they serve completely different purposes. Wire = reactive data fetching. connectedCallback = imperative setup logic on DOM insertion! 🔄
📋 Wire vs connectedCallback Comparison
Factor@wireconnectedCallback()
Triggered byFramework (reactive params)DOM insertion
Called automatically?✅ Yes✅ Yes
PurposeFetch & sync Salesforce dataRun JS logic on mount
Reactive?✅ Re-runs on param change❌ Runs once only
Fires first?✅ Wire fires BEFORE connectedCallbackFires AFTER wire
DML via Apex?❌ Not allowed✅ Allowed (imperative)
📌 When to Prefer connectedCallback
  • Subscribe to LMS channel
  • Add DOM event listeners
  • Set default/initial values
  • Conditional data fetch based on @api value (if/else logic)
  • Don't query DOM here — DOM not yet rendered in connectedCallback
🎤 One-Line Answer for Interview
"Wire automatically fetches and keeps Salesforce data reactive — connectedCallback runs custom JS logic once the component mounts. Both are called automatically, but wire fires first. Wire is for data; connectedCallback is for setup like LMS subscriptions and default values."
Q
Question 08 · 🟠 Intermediate
What are the LWC lifecycle hooks? Explain all of them with the difference.
✅ Answer
LWC has 5 lifecycle hooks — each firing at a specific stage of the component's life in the DOM. Know all 5 for interviews! 🔄
📋 All 5 Lifecycle Hooks
HookWhen It FiresCommon Use
constructor()Component class instantiatedInitialize local variables, call super()
connectedCallback()Component inserted into DOMLMS subscribe, set defaults, imperative logic
renderedCallback()After every render/re-renderDOM manipulation — use flag to prevent loops
disconnectedCallback()Component removed from DOMUnsubscribe LMS, clean up listeners
errorCallback(error, stack)Child component throws errorError boundary — parent only
// Execution Order: constructor() → @wire executes ← FIRST → connectedCallback() ← SECOND → render() → renderedCallback() ...re-renders... disconnectedCallback() ← on removal
🔑 Key Points for Interviewer
  • 🔥renderedCallback fires on EVERY re-render — always use a boolean flag to prevent infinite loops
  • 🔥constructor — always call super() as first line — mandatory
  • 💡errorCallback — only fires in parent components when a child throws — like a try-catch at component tree level
  • 💡Always pair connectedCallback (subscribe) with disconnectedCallback (unsubscribe) to prevent memory leaks
🎤 One-Line Answer for Interview
"LWC has 5 lifecycle hooks — constructor, connectedCallback, renderedCallback, disconnectedCallback, and errorCallback. Wire fires before connectedCallback. renderedCallback fires on every re-render so always use a guard flag. Always unsubscribe in disconnectedCallback to prevent memory leaks."
Q
Question 09 · 🟠 Intermediate
Can we write @wire inside connectedCallback()? — Answer from Mohit is NO.
✅ Answer — NO ❌
@wire is a class-level decorator processed at compile time — it cannot be placed inside a method. connectedCallback() is a runtime method. The two cannot be mixed! ❌
// ❌ WRONG — Will throw a compile error connectedCallback() { @wire(getRecord, { recordId: this.recordId }) wiredRecord; } // ✅ CORRECT — Wire at class body level @wire(getRecord, { recordId: '$recordId', fields: FIELDS }) wiredRecord; connectedCallback() { // Your imperative logic here — no wire allowed this.defaultDate = new Date().toISOString(); }
📋 What to Do Instead
NeedSolution
Data on page load reactivelyUse @wire at class level ✅
Data on page load imperativelyUse imperative Apex in connectedCallback ✅
Data on button clickUse imperative Apex in event handler ✅
Conditional data fetchImperative Apex with if/else logic ✅
🎤 One-Line Answer for Interview
"No — @wire is a class-level decorator processed at compile time and cannot be placed inside connectedCallback() or any method. If data is needed on mount with conditional logic, use imperative Apex inside connectedCallback() instead."
Q
Question 10 · 🔴 Advanced
What is Lightning Message Service (LMS)? How does it work?
✅ Answer
LMS is Salesforce's publish-subscribe messaging system that enables communication between completely unrelated LWC, Aura, and Visualforce components on the same page — without any parent-child relationship! 📡
📋 LMS Features
FeatureDetails
Communication ScopeEntire App Page — unrelated components
Works acrossLWC ↔ LWC, LWC ↔ Aura, LWC ↔ Visualforce
Channel defined in.messageChannel metadata file
Subscribesubscribe() in connectedCallback
Unsubscribeunsubscribe() in disconnectedCallback
Publishpublish() from any component
// Publisher import { publish, MessageContext } from 'lightning/messageService'; import MY_CHANNEL from '@salesforce/messageChannel/MyChannel__c'; @wire(MessageContext) messageContext; publish(this.messageContext, MY_CHANNEL, { recordId: this.recordId }); // Subscriber import { subscribe, unsubscribe, APPLICATION_SCOPE } from 'lightning/messageService'; connectedCallback() { this.subscription = subscribe( this.messageContext, MY_CHANNEL, (msg) => this.handleMessage(msg), { scope: APPLICATION_SCOPE } ); } disconnectedCallback() { unsubscribe(this.subscription); // ← Always clean up! }
🔑 Key Points for Interviewer
  • 🔥LMS replaced the old Pub-Sub community pattern — always recommend LMS in interviews
  • 💡APPLICATION_SCOPE = truly global across entire app page
  • 💡Always unsubscribe in disconnectedCallback to prevent memory leaks
  • ⚠️Does NOT work across different browser tabs or pages
🎤 One-Line Answer for Interview
"LMS is Salesforce's official publish-subscribe messaging system that enables communication between unrelated LWC, Aura, and Visualforce components on the same page using a Message Channel metadata file — it replaced the old community Pub-Sub pattern."
Q
Question 11 · 🟠 Intermediate
What are Component Events and Application Events in Aura? What are the LWC equivalents?
✅ Answer
Component Events = scoped parent-child bubbling. Application Events = global broadcast. In LWC, Custom Events replaced Component Events and LMS replaced Application Events! 📢
📋 Aura Events vs LWC Equivalents
Aura ConceptScopeLWC Equivalent
Component EventBubbles up parent-child onlyCustom Events + dispatchEvent
Application EventBroadcasts to ALL handlers in appLightning Message Service (LMS)
🔑 Key Points for Interviewer
  • 🔥Application Events are an anti-pattern in large orgs — hard to debug, LMS replaced them
  • 💡LWC intentionally removed Application Events — forced developers to use LMS
  • 💡Custom Events with bubbles:true + composed:true can cross shadow DOM boundaries
🎤 One-Line Answer for Interview
"Aura Component Events bubble through parent-child hierarchy — replaced by Custom Events in LWC. Aura Application Events broadcast globally to all listeners — replaced by Lightning Message Service in LWC. Both replacements are more controlled and performant."
Q
Question 12 · 🟢 Basic
What is used to display Toast notifications in LWC?
✅ Answer
Use ShowToastEvent from lightning/platformShowToastEvent — fire it as a custom event using dispatchEvent()! 🍞
import { ShowToastEvent } from 'lightning/platformShowToastEvent'; showToast() { this.dispatchEvent(new ShowToastEvent({ title: 'Success', message: 'Record saved successfully!', variant: 'success', // success | error | warning | info mode: 'dismissable' // dismissable | sticky | pester })); }
📋 Variant Options
VariantIconUse Case
success✅ Green checkmarkRecord saved, action completed
error❌ Red XApex error, validation failure
warning⚠️ Yellow warningPartial success or caution
infoℹ️ Blue infoGeneral information
🔑 Key Points for Interviewer
  • 🔥Works on Record Pages, App Pages, Home Pages — does NOT work in Utility Bar or Experience Cloud
  • 💡mode: 'sticky' keeps toast until user dismisses — use for important messages
  • 💡Toast is fired as an event — not a method call
🎤 One-Line Answer for Interview
"ShowToastEvent from lightning/platformShowToastEvent — dispatched via this.dispatchEvent() with title, message, variant (success/error/warning/info), and mode. Does not work in Utility Bar or Experience Cloud components."
Q
Question 13 · 🟠 Intermediate
Are Quick Actions supported for LWC components? How do you reference the record ID from a Quick Action?
✅ YES — Quick Actions are supported for LWC!
Yes — LWC supports Quick Actions. Declare @api recordId in the component and Salesforce automatically injects the current record's ID when launched from a Quick Action! ⚡
📋 Setup Requirements
// .js-meta.xml — required metadata <targets> <target>lightning__RecordAction</target> </targets> <targetConfigs> <targetConfig targets="lightning__RecordAction"> <actionType>ScreenAction</actionType> </targetConfig> </targetConfigs> // JS — auto-populated by Salesforce import { LightningElement, api } from 'lwc'; import { CloseActionScreenEvent } from 'lightning/actions'; export default class MyQuickAction extends LightningElement { @api recordId; // ← Auto-injected by Salesforce! handleClose() { this.dispatchEvent(new CloseActionScreenEvent()); } }
🔑 Key Points for Interviewer
  • 🔥Property name must be exactly recordId — case-sensitive
  • 💡Use CloseActionScreenEvent to programmatically close the Quick Action modal
  • 💡@api objectApiName also auto-populates with the object type
  • ⚠️If launched from Global Quick Action (no record), recordId will be undefined
🎤 One-Line Answer for Interview
"Yes — LWC supports Quick Actions by targeting lightning__RecordAction in metadata. Declare @api recordId and Salesforce automatically injects the record ID. Use CloseActionScreenEvent to close the modal programmatically."
Q
Question 14 · 🟠 Intermediate
How to send data from Parent to Child in LWC?
✅ Answer
Use @api decorator in the child — parent passes data via HTML attribute binding. LWC enforces strict one-way top-down data flow! ⬇️
// Child — expose with @api import { LightningElement, api } from 'lwc'; export default class ChildComponent extends LightningElement { @api accountName; // camelCase in JS @api refresh() { ... } // Parent can also call methods } // Parent HTML — kebab-case in HTML <c-child-component account-name={selectedName}></c-child-component> // ^^^^^^^^^^^^^ camelCase → kebab-case in HTML
📋 Communication Direction Summary
DirectionMethod
Parent → Child (data)@api property — HTML attribute binding
Parent → Child (method)@api method — parent calls via querySelector
Child → ParentCustomEvent + dispatchEvent
Unrelated componentsLMS (Lightning Message Service)
🔑 Key Points for Interviewer
  • 🔥camelCase in JS → kebab-case in HTML — always! (accountName → account-name)
  • 💡Child should NEVER mutate @api properties — read-only from child's perspective
  • 💡Data flows DOWN via @api, events flow UP via CustomEvent — this is the core LWC pattern
🎤 One-Line Answer for Interview
"Parent sends data to child by declaring @api properties in the child and binding values in the parent's HTML template — LWC enforces one-way top-down data flow. camelCase in JS maps to kebab-case in HTML."
Q
Question 15 · 🟠 Intermediate
How to accomplish Child to Parent communication in LWC? Can a child directly access a parent's method?
✅ Answer
Child communicates up via CustomEvent + dispatchEvent. A child CANNOT directly call a parent's method — it fires an event and the parent decides what to do! ⬆️
// CHILD — dispatches event handleButtonClick() { this.dispatchEvent( new CustomEvent('accountselect', { detail: { id: this.account.Id }, bubbles: true, // bubbles up DOM tree composed: false // stays within shadow boundary }) ); } // PARENT HTML — listens with on{eventname} <c-child-component onaccountselect={handleAccountSelect}></c-child-component> // PARENT JS — handles and calls its own method handleAccountSelect(event) { const selectedId = event.detail.id; this.myParentMethod(selectedId); // ← parent calls its OWN method }
🔑 Key Points for Interviewer
  • 🔥Event name in JS: camelCase (accountSelect) → Listener in HTML: lowercase prefixed with on (onaccountselect)
  • 🔥Child CANNOT do this.parent.someMethod() — no parent reference exists in LWC
  • 💡Use event.detail to pass payload to parent — can be any serializable data
  • 💡This is by design — LWC enforces encapsulation. Tight coupling between parent/child is an anti-pattern
🎤 One-Line Answer for Interview
"Child fires a CustomEvent with payload in detail — parent listens via on{eventname} in HTML and handles via a JS method that calls its own internal logic. A child cannot directly access a parent's method — LWC intentionally enforces encapsulation."
Q
Question 16 · 🟠 Intermediate
How can we navigate a user from LWC to a Record Detail Page?
✅ Answer
Use NavigationMixin from lightning/navigation — the component class must extend NavigationMixin(LightningElement)! 🧭
import { LightningElement } from 'lwc'; import { NavigationMixin } from 'lightning/navigation'; export default class MyComponent extends NavigationMixin(LightningElement) { navigateToRecord() { this[NavigationMixin.Navigate]({ type: 'standard__recordPage', attributes: { recordId: this.recordId, actionName: 'view' // view | edit | clone } }); } }
📋 Navigation Types
Destinationtype value
Record Pagestandard__recordPage
Object List Viewstandard__objectPage
New Record Formstandard__objectPage + actionName: 'new'
External URLstandard__webPage
App Page (Nav Item)standard__navItemPage
🔑 Key Points for Interviewer
  • 🔥NavigationMixin is a Higher Order Component (HOC) pattern — wraps LightningElement
  • 💡NavigationMixin.GenerateUrl builds a URL without navigating — useful for anchor tags
  • Never use window.location.href — breaks Salesforce SPA navigation
  • 💡Works only in Lightning Experience — not in Classic
🎤 One-Line Answer for Interview
"Use NavigationMixin from lightning/navigation — extend NavigationMixin(LightningElement) and call this[NavigationMixin.Navigate]() with a page reference object. Never use window.location.href as it breaks Salesforce SPA navigation."
Q
Question 17 · 🟠 Intermediate
What is render() in LWC? Why do we use it?
✅ Answer
render() is a lifecycle method used to conditionally switch between completely different HTML templates based on component state! 🔀
import { LightningElement } from 'lwc'; import templateA from './templateA.html'; // Additional HTML file import templateB from './templateB.html'; // Additional HTML file export default class MyComponent extends LightningElement { showTableView = true; render() { return this.showTableView ? templateA : templateB; } }
📋 render() vs lwc:if
Factorrender()lwc:if directive
Use caseCompletely different templatesShow/hide within same template
How many HTML filesMultiple .html files in bundleSingle HTML file
ComplexityMore setup neededSimple and inline
When to useError state vs Success state vs LoadingMost conditional rendering
🔑 Key Points for Interviewer
  • 🔥render() is called by the framework — never call this.render() manually
  • 💡Additional HTML files must be in the same component folder
  • 💡render() is rarely needed — lwc:if covers most use cases. Use render() for completely different layout structures only
🎤 One-Line Answer for Interview
"render() returns an HTML template reference allowing the component to dynamically switch between entirely different templates — rarely needed since lwc:if handles most conditional rendering. Use it for completely different UI states like loading, error, and success screens."
Q
Question 18 · 🟢 Basic
Can I get the current user ID in LWC without using Apex?
✅ YES — No Apex needed!
Use @salesforce/user/Id static import — Salesforce injects it at compile time with zero server calls! 👤
import { LightningElement } from 'lwc'; import USER_ID from '@salesforce/user/Id'; import USER_NAME from '@salesforce/user/Name'; export default class MyComponent extends LightningElement { userId = USER_ID; // Available immediately — no async! userName = USER_NAME; }
📋 Available @salesforce/user Modules
ModuleReturns
@salesforce/user/IdCurrent logged-in User's ID
@salesforce/user/NameUser's Name
@salesforce/user/IsGuestBoolean — is guest user?
@salesforce/userPermission/PermNameBoolean — has permission?
@salesforce/customPermission/PermNameBoolean — has custom permission?
🎤 One-Line Answer for Interview
"Import USER_ID from '@salesforce/user/Id' — Salesforce injects it at compile time as a constant with no Apex call or async required. For full user record details, use @wire(getRecord) or Apex."
Q
Question 19 · 🟠 Intermediate
What is the difference between @track and @api in LWC?
✅ Answer
Both are reactive — but completely different scopes. @track = private internal deep reactivity. @api = public interface accessible from outside! 🔐
Factor@track@api
ScopeInternal — private to componentPublic — accessible from outside
PurposeDeep reactivity for objects/arraysExpose to parent or App Builder
Accessible from parent?❌ No✅ Yes
Mutable inside component?✅ Yes freely⚠️ Avoid — anti-pattern
Needed for primitives?❌ No — auto-reactive since Spring '20❌ Already reactive
🌍 Real World Example at XYZ Company

In the Quote Line Items LWC: @api recordId received the Quote ID from the parent record page (public). @track lineItems = [] tracked the array reactively as rows were added/removed. @wire(getQuoteLineItems, { quoteId: '$recordId' }) fetched line items reactively when the record changed.

🎤 One-Line Answer for Interview
"@track is for internal deep reactivity of objects and arrays — private to the component. @api exposes a property publicly to parent components and App Builder — both are reactive, but @api creates the public interface while @track is strictly internal state management."
Q
Question 20 · 🔴 Advanced
What does cacheable=true annotation mean on an Apex method? What are its limitations?
✅ Answer
cacheable=true allows the wire service to cache the Apex result client-side AND is mandatory for using @wire. But DML is strictly NOT allowed in cacheable methods! 💾
// ✅ Cacheable — use with @wire @AuraEnabled(cacheable=true) public static List<Account> getAccounts(String searchKey) { return [SELECT Id, Name FROM Account WHERE Name LIKE :searchKey]; } // ❌ NOT cacheable — DML inside — use imperatively only @AuraEnabled public static void saveAccount(Account acc) { insert acc; // DML allowed here }
📋 cacheable=true vs default (false)
Factorcacheable=truecacheable=false (default)
Use with @wire?✅ Yes — required❌ No — use imperative
Cache result?✅ Yes — client cache❌ No
DML operations?❌ NOT allowed✅ Allowed
Performance✅ Faster — cachedHits server every time
Refresh stale cacherefreshApex() needed after DMLAlways fresh
🎤 One-Line Answer for Interview
"cacheable=true enables client-side caching of the Apex result and is mandatory for @wire — but DML is strictly not allowed in cacheable methods. Use refreshApex() after imperative DML to invalidate the stale wire cache."
Q
Question 21 · 🟢 Basic
Can we deploy a component with an empty CSS file?
✅ YES — Perfectly valid!
An empty CSS file is completely valid and deployable. The CSS file is optional in an LWC bundle — only HTML and JS are mandatory! 🎨
ScenarioDeployable?
No CSS file at all✅ Yes
Empty CSS file✅ Yes
CSS with valid rules✅ Yes
CSS with syntax errors❌ No — compile error
🔑 Key Points for Interviewer
  • 🔥Only HTML and JS files are mandatory in an LWC bundle — CSS, meta XML, SVG are all optional
  • 💡LWC uses Shadow DOM — CSS in the file is scoped to the component only. Styles never leak out
  • 💡Cannot use global selectors like body or div to style outside the component
🎤 One-Line Answer for Interview
"Yes — an empty CSS file deploys without errors. The CSS file is optional in LWC — only HTML and JS are required. LWC uses Shadow DOM so CSS is automatically scoped to the component and never leaks out."
Q
Question 22 · 🟠 Intermediate
Do we have a force:createRecord equivalent in LWC?
✅ YES — Use NavigationMixin!
The LWC equivalent of force:createRecord is NavigationMixin.Navigate with standard__objectPage and actionName: 'new'! 🆕
import { NavigationMixin } from 'lightning/navigation'; export default class MyComponent extends NavigationMixin(LightningElement) { // Basic new record form createNewAccount() { this[NavigationMixin.Navigate]({ type: 'standard__objectPage', attributes: { objectApiName: 'Account', actionName: 'new' } }); } // With pre-filled default values createWithDefaults() { this[NavigationMixin.Navigate]({ type: 'standard__objectPage', attributes: { objectApiName: 'Opportunity', actionName: 'new' }, state: { defaultFieldValues: 'Name=Test Opp,StageName=Prospecting' } }); } }
AuraLWC Equivalent
force:createRecordNavigationMixin + actionName: 'new'
force:editRecordNavigationMixin + actionName: 'edit'
force:navigateToRecordNavigationMixin + standard__recordPage
🎤 One-Line Answer for Interview
"The LWC equivalent of force:createRecord is NavigationMixin.Navigate with type: 'standard__objectPage' and actionName: 'new'. Use defaultFieldValues in the state object to pre-populate fields on the new record form."
Q
Question 23 · 🟠 Intermediate
What are the different ways LWC can talk to an @AuraEnabled Apex class?
✅ Answer
Two ways — @wire (declarative, reactive) and Imperative (programmatic, Promise-based). Choice depends on cacheable, DML, and when data is needed! 🔌
// Way 1 — @wire (declarative) // Apex must be cacheable=true, NO DML @wire(getAccounts, { searchKey: '$searchKey' }) wiredAccounts({ data, error }) { if (data) this.accounts = data; } // Way 2 — Imperative (programmatic) // Works with or without cacheable, DML allowed handleSave() { saveOrder({ orderId: this.recordId }) .then(result => this.showToast('Saved!', '', 'success')) .catch(error => this.showToast('Error', error.body.message, 'error')); }
📋 Wire vs Imperative
FactorWireImperative
Triggered byFramework automaticallyYou explicitly (button, event)
cacheable=true required?✅ Yes — mandatory❌ No — optional
DML allowed?❌ No✅ Yes
Reactive?✅ Re-runs on $ param change❌ No
Returns{data, error} objectPromise
🎤 One-Line Answer for Interview
"LWC has two ways to call Apex: @wire for declarative reactive read-only data (requires cacheable=true), and imperative calls that return Promises for DML operations and user-triggered logic. The choice depends on when and how data is needed."
Q
Question 24 · 🔴 Advanced
If we call Apex imperatively, what is the requirement for the wire adapter? Can you explain?
✅ Answer
Imperative Apex and @wire are completely independent. If you call Apex imperatively, @wire has NO requirement — but if you use @wire, the Apex method MUST be cacheable=true! 🎯
📋 Requirements Comparison
RequirementImperative ApexWire Adapter
@AuraEnabled✅ Required✅ Required
cacheable=true❌ Optional✅ MANDATORY
DML allowed?✅ Yes❌ No
ReturnsPromise{data, error}
Called byYou explicitlyFramework automatically
Reactive?❌ No✅ Yes
// Imperative — only @AuraEnabled needed @AuraEnabled public static void saveOrderLine(OrderLine__c line) { insert line; // DML is fine ✅ } // Wire — MUST have cacheable=true @AuraEnabled(cacheable=true) public static List<OrderLine__c> getOrderLines(Id orderId) { return [SELECT Id, Product__c FROM OrderLine__c WHERE Order__c = :orderId]; }
🌍 Real World Example at XYZ Company

getQuoteLineItems was marked cacheable=true → used with @wire for display. saveQuoteLineItem was NOT cacheable → called imperatively on Save button since it performed DML. Two different methods, two different approaches — zero conflict. After DML, refreshApex() was called to refresh the wire cache.

🎤 One-Line Answer for Interview
"Imperative Apex only requires @AuraEnabled — cacheable=true is optional and DML is allowed. Wire adapters require @AuraEnabled(cacheable=true) and cannot perform DML. They are independent — choose based on use case. This is the most important LWC rule."
Q
Question 25 · 🟠 Intermediate
What is the difference between Wire and Imperative method in LWC?
✅ Answer
Wire = declarative, reactive, auto-executed by framework. Imperative = explicit, Promise-based, you control when it runs. Choose based on when and how data is needed! ⚡
FactorWireImperative
TriggerFramework auto-managesDeveloper manually triggers
cacheable=true✅ Required❌ Not required
DML❌ Not allowed✅ Allowed
Reactive to params✅ Yes — $ prefix❌ No
Return type{data, error}Promise
Best forRead on page load, reactive filtersSave, delete, user-triggered actions
Error handlingerror property in result.catch() or try/catch
🎤 One-Line Answer for Interview
"Wire is declarative and reactive — automatically executed by the framework when $ parameters change, requires cacheable=true, no DML. Imperative is explicit — you call it on demand, returns a Promise, supports DML. Choose wire for reactive reads on load, imperative for DML and user-triggered operations."
Q
Question 26 · 🟠 Intermediate
What is the difference between Wire Property and Wire Function?
✅ Answer
Both use @wire — the difference is how you receive and handle the result. Wire property for simple template binding; wire function when you need to process data on arrival! 🎯
// Wire PROPERTY — stores {data, error} directly @wire(getRecord, { recordId: '$recordId', fields: FIELDS }) account; // Access: this.account.data / this.account.error // Wire FUNCTION — gives you a handler to process data wiredAccountResult; // save reference for refreshApex @wire(getRecord, { recordId: '$recordId', fields: FIELDS }) wiredAccount(result) { this.wiredAccountResult = result; // save for refreshApex if (result.data) { // Transform, validate, set multiple properties this.accountName = result.data.fields.Name.value; this.accountType = result.data.fields.Type.value; } if (result.error) { this.error = result.error; } }
FactorWire PropertyWire Function
Syntax@wire(fn, params) propName;@wire(fn, params) handlerFn({data, error})
Logic on receive?❌ No✅ Yes — full JS logic
refreshApex?✅ Pass property directly✅ Save result reference first
Best forSimple display in templateTransform, validate, set multiple properties
🎤 One-Line Answer for Interview
"Wire property stores {data, error} directly on a property for template binding — concise but no processing logic. Wire function passes data into a handler where you can write transformation logic, set multiple properties, or validate — choose function when data processing is needed on arrival."
Q
Question 27 · 🟢 Basic
Can we perform DML inside Wire? Why or why not?
✅ Answer — NO ❌
DML is strictly NOT allowed inside @wire. Wire methods must be cacheable=true — and Salesforce prohibits DML in cacheable methods because they are read-only and repeatable! 🚫
// ❌ WRONG — DML inside cacheable — RUNTIME ERROR @AuraEnabled(cacheable=true) public static Account saveAccount(Account acc) { insert acc; // System.QueryException: DML not allowed in read-only context return acc; } // ✅ CORRECT — DML in non-cacheable → call imperatively @AuraEnabled public static Account saveAccount(Account acc) { insert acc; return acc; }
🔑 Key Points for Interviewer
  • 🔥Wire = read-only data fetching — never DML. This is the #1 rule of wire
  • 💡DML always goes through imperative Apex calls
  • 💡After imperative DML, use refreshApex() to refresh the wire cache
🎤 One-Line Answer for Interview
"No — DML is not allowed inside @wire because wire methods require cacheable=true, which enforces a read-only context. Salesforce throws a runtime error. All DML must go through imperative Apex. Use refreshApex() afterwards to sync the wire cache."
Q
Question 28 · 🟠 Intermediate
What is Lightning Data Service (LDS)? Write the LDS syntax to fetch a record.
✅ Answer
LDS is Salesforce's client-side data layer for CRUD operations on records without writing Apex. It auto-enforces FLS, sharing rules, and maintains a shared cache across components! 🏗️
📌 LDS Fetch Record Syntax
import { LightningElement, api, wire } from 'lwc'; import { getRecord, getFieldValue } from 'lightning/uiRecordApi'; import ACCOUNT_NAME from '@salesforce/schema/Account.Name'; import ACCOUNT_PHONE from '@salesforce/schema/Account.Phone'; const FIELDS = [ACCOUNT_NAME, ACCOUNT_PHONE]; export default class AccountDetail extends LightningElement { @api recordId; @wire(getRecord, { recordId: '$recordId', fields: FIELDS }) account; // { data, error } get accountName() { return getFieldValue(this.account.data, ACCOUNT_NAME); } get accountPhone() { return getFieldValue(this.account.data, ACCOUNT_PHONE); } }
📋 LDS Capabilities
FeatureLDS
Requires Apex?❌ No
Respects FLS & Sharing?✅ Automatically
Client-side cache?✅ Yes — shared across components
Auto-updates all components?✅ Yes — same record
🎤 One-Line Answer for Interview
"LDS is Salesforce's client-side data layer that enables record CRUD without Apex — automatically respects FLS, sharing rules, and maintains a shared cache. Use getRecord with @wire and schema imports for compile-time field validation. getFieldValue safely extracts field values from the wire result."
Q
Question 29 · 🔴 Advanced
What is the uiRecordApi module? How do you create, update, and delete records using LDS?
✅ Answer
uiRecordApi is the LDS module providing wire adapters and functions for programmatic record operations — with compile-time field validation via schema imports! 🔧
import { getRecord, // Wire adapter — reactive record fetch createRecord, // Returns Promise updateRecord, // Returns Promise deleteRecord, // Returns Promise getFieldValue, // Extract raw value getFieldDisplayValue // Extract formatted value (currency/date) } from 'lightning/uiRecordApi'; import ACCOUNT_OBJECT from '@salesforce/schema/Account'; import NAME_FIELD from '@salesforce/schema/Account.Name'; // Create async handleCreate() { const fields = {}; fields[NAME_FIELD.fieldApiName] = this.accountName; await createRecord({ apiName: ACCOUNT_OBJECT.objectApiName, fields }); } // Update async handleUpdate() { const fields = { Id: this.recordId }; fields[NAME_FIELD.fieldApiName] = this.newName; await updateRecord({ fields }); } // Delete async handleDelete() { await deleteRecord(this.recordId); }
🔑 Key Points for Interviewer
  • 🔥Schema imports (@salesforce/schema) = compile-time validation — typos in field names caught at deploy time, not runtime
  • 💡getFieldValue = raw value. getFieldDisplayValue = formatted value (e.g., ₹1,000.00 for currency)
  • 💡createRecord/updateRecord/deleteRecord return Promises — use async/await or .then()
  • 💡LDS cache auto-updates when any component creates/updates/deletes — all components showing that record refresh instantly
🎤 One-Line Answer for Interview
"uiRecordApi provides getRecord (wire), createRecord, updateRecord, and deleteRecord — with schema imports for compile-time field validation, automatic FLS enforcement, and LDS cache integration. createRecord/updateRecord/deleteRecord return Promises."
Q
Question 30 · 🔴 Advanced
When should you use LDS and when should you use Apex for Insert, Update, Delete?
✅ Answer
Use LDS for simple single-record CRUD — use Apex when you need logic, multiple objects, callouts, or bulk operations! 🎯
📋 Decision Guide
ScenarioUse
Simple single record create/update/delete✅ LDS
FLS must be enforced automatically✅ LDS (auto-enforced)
LDS cache auto-update needed✅ LDS
Multiple objects in one transaction✅ Apex
Callout after DML✅ Apex
Complex validation beyond field rules✅ Apex
Bulk insert/update (multiple records)✅ Apex
Cross-object operations✅ Apex
🌍 Real World Example at XYZ Company

LDS used → Updating a single Account's KYC status field from a Quick Action — simple field update, FLS auto-enforced, zero Apex written, cache auto-updated. Apex used → Creating a Proforma Invoice — required inserting the invoice record, linking line items, generating PDF, and triggering a BC API callout, all in one transaction. LDS cannot do any of that.

🎤 One-Line Answer for Interview
"Use LDS for simple single-record CRUD where automatic FLS enforcement and cache sync are enough. Use Apex for multi-object transactions, custom business logic, callouts, or bulk operations — and remember to call refreshApex() after Apex DML to sync the wire cache."
Q
Question 31 · 🔴 Advanced
How to get picklist values of a field in LWC without using Apex?
✅ Answer
Use getPicklistValues from lightning/uiObjectInfoApi — first wire getObjectInfo for the recordTypeId, then use it reactively in getPicklistValues. Zero Apex needed! 📋
import { LightningElement, wire } from 'lwc'; import { getPicklistValues, getObjectInfo } from 'lightning/uiObjectInfoApi'; import ACCOUNT_OBJ from '@salesforce/schema/Account'; import TYPE_FIELD from '@salesforce/schema/Account.Type'; export default class AccountPicklist extends LightningElement { // Step 1 — Get object info to retrieve default recordTypeId @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJ }) objectInfo; // Step 2 — Get picklist values using reactive recordTypeId @wire(getPicklistValues, { recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName: TYPE_FIELD }) typePicklistValues; get typeOptions() { if (!this.typePicklistValues?.data) return []; return this.typePicklistValues.data.values.map(item => ({ label: item.label, value: item.value })); } }
📋 Alternatively — Fully Declarative (Zero JS)
<!-- lightning-record-edit-form auto-loads picklist values --> <lightning-record-edit-form object-api-name="Account"> <lightning-input-field field-name="Type"></lightning-input-field> <lightning-button type="submit" label="Save"></lightning-button> </lightning-record-edit-form>
🎤 One-Line Answer for Interview
"Use getPicklistValues from lightning/uiObjectInfoApi — first wire getObjectInfo to get the defaultRecordTypeId, then pass it reactively to getPicklistValues. Map the .values array to label/value pairs for a lightning-combobox. Zero Apex required — LDS handles it all."
Q
Question 32 · 🟠 Intermediate
What is the spread operator and what are its uses in LWC?
✅ Answer
The spread operator (...) expands arrays/objects and is heavily used in LWC for immutable state updates — the standard way to update @track arrays without losing reactivity! 📦
// On Arrays — add item immutably (creates new array reference) handleAddItem() { this.lineItems = [...this.lineItems, newItem]; // ← new reference = re-render ✅ } // On Arrays — remove item handleRemoveItem(id) { this.lineItems = this.lineItems.filter(item => item.Id !== id); } // On Objects — update nested property immutably handleFilterChange(event) { this.filters = { ...this.filters, [event.target.name]: event.target.value }; } // Clone wire result (make mutable) wiredAccount({ data }) { if (data) this.account = { ...data }; // shallow clone } // Run parallel Apex calls const ids = [...this.selectedIds]; // Set to Array const [opps, cases] = await Promise.all([getOpps({ ids }), getCases({ ids })]);
🔑 Key Points for Interviewer
  • 🔥Spread creates a shallow copy — nested objects are still referenced
  • 💡For deep copy → use JSON.parse(JSON.stringify(obj))
  • 💡Spread is the standard way to update @track arrays/objects immutably in LWC
  • 💡Creating a new array/object reference with spread triggers @track reactivity
🎤 One-Line Answer for Interview
"The spread operator expands arrays/objects and is essential in LWC for immutable state updates — adding/removing from @track arrays, updating nested object properties, and cloning wire data for mutation. Creating new references with spread triggers @track reactivity."
Q
Question 33 · 🟠 Intermediate
How do I retrieve elements on the basis of ID in LWC? Why can't we use document.getElementById()?
✅ Answer
document.getElementById() does NOT work in LWC — Shadow DOM blocks global document queries. Use this.template.querySelector() or the modern lwc:ref directive! 🔍
// ❌ FAILS — Shadow DOM blocks global queries document.getElementById('myInput'); // null always // ✅ Way 1 — data-id attribute (Most Common Pattern) // HTML: <input data-id="nameInput" type="text" /> const input = this.template.querySelector('[data-id="nameInput"]'); // ✅ Way 2 — querySelector by class // HTML: <div class="result-box"></div> const box = this.template.querySelector('.result-box'); // ✅ Way 3 — querySelectorAll for multiple const allInputs = this.template.querySelectorAll('.line-input'); allInputs.forEach(inp => console.log(inp.value)); // ✅ Way 4 — lwc:ref (Modern Recommended Approach) // HTML: <input lwc:ref="nameInput" type="text" /> handleFocus() { this.refs.nameInput.focus(); // Clean, no selector string needed }
📋 Method Comparison
MethodReliabilityUse Case
document.getElementById()❌ Never worksDon't use in LWC
this.template.querySelector('#id')⚠️ Fragile — id transformsAvoid
this.template.querySelector('[data-id="x"]')✅ ReliableStandard pattern
this.template.querySelector('.class')✅ ReliableClass-based selection
this.refs.refName (lwc:ref)✅ BestModern recommended
🔑 Key Points for Interviewer
  • 🔥Shadow DOM = document methods are blind to component's DOM — this is the core reason
  • 💡LWC intentionally transforms id attributes at runtime to prevent global id conflicts — so even querySelector('#id') is unreliable
  • 💡Never query DOM in constructor() — DOM not ready yet. Safe in renderedCallback() or event handlers
  • 💡lwc:ref + this.refs is newer (Summer '23) — know both approaches
🎤 One-Line Answer for Interview
"document.getElementById() always returns null in LWC because Shadow DOM encapsulates the component's DOM from global queries. Use this.template.querySelector('[data-id=x]') as the standard pattern, or the modern lwc:ref + this.refs.refName approach for direct, clean element access."
Q
Question 34 · 🔴 Advanced
What is the difference between LMS and Pub-Sub in LWC?
✅ Answer
LMS is Salesforce's official standard — Pub-Sub was a community-built workaround. LMS replaced Pub-Sub entirely and supports Aura + Visualforce too! 📡
FeatureLMS (Lightning Message Service)Pub-Sub (Old Pattern)
Official Salesforce?✅ Yes — standard feature❌ No — community utility
Works acrossLWC, Aura, VisualforceLWC only
SetupMessage Channel metadata fileImport pubsub.js module
Survives navigation?✅ Yes (APPLICATION_SCOPE)❌ No
Memory managementAuto-managed with unsubscribeManual cleanup required
Recommended today?✅ Yes — always use LMS❌ Deprecated
🔑 Key Points for Interviewer
  • 🔥Always recommend LMS in interviews — Pub-Sub is legacy and deprecated
  • 💡APPLICATION_SCOPE = truly global across the entire app page, not just a region
  • 💡LMS Message Channel is a metadata file deployed to the org — not just a JS utility
🎤 One-Line Answer for Interview
"LMS is Salesforce's official cross-framework messaging system with metadata channels, broader scope across LWC/Aura/Visualforce, and APPLICATION_SCOPE support — Pub-Sub was a community workaround that LMS has fully replaced. Always recommend LMS."
Q
Question 35 · 🟠 Intermediate
What is the difference between Event Bubbling and Event Capturing in LWC?
✅ Answer
Capturing flows top-down (document → target). Bubbling flows bottom-up (target → document). Bubbling is the default and most commonly used in LWC! 🫧
// Event phases visualization: // Capturing Phase ↓ (document → parent → target) // Target fires the event // Bubbling Phase ↑ (target → parent → document) // Bubbling (default) — most common element.addEventListener('click', handler); // bubbling element.addEventListener('click', handler, false); // bubbling (explicit) // Capturing — opt-in element.addEventListener('click', handler, true); // capturing // In LWC — CustomEvent with bubbles this.dispatchEvent(new CustomEvent('select', { bubbles: true, // ← event bubbles up DOM tree composed: true // ← crosses shadow DOM boundary }));
FactorEvent CapturingEvent Bubbling
DirectionTop → Down (document to target)Bottom → Up (target to document)
PhasePhase 1Phase 3
Default?❌ Must opt in✅ Default behavior
Common in LWC?Rare✅ Very common
🔑 Key Points for Interviewer
  • 🔥99% of interview questions refer to bubbling — capturing is rarely used
  • 💡composed: true in LWC is needed to bubble across shadow DOM boundaries between components
  • 💡stopPropagation() stops bubbling. stopImmediatePropagation() also stops other listeners on the same element
🎤 One-Line Answer for Interview
"Event capturing flows top-down from document to target; event bubbling flows bottom-up from target to document — bubbling is the default. In LWC, use bubbles:true to bubble and composed:true to cross shadow DOM boundaries. stopPropagation() stops the event from bubbling further."
Q
Question 36 · 🟠 Intermediate
What is the difference between event.stopPropagation() and event.preventDefault() in LWC?
✅ Answer
Both control event behavior — but they do completely different things. stopPropagation = stop bubbling. preventDefault = cancel browser's default action! 🚦
MethodWhat It StopsCommon Use in LWC
event.stopPropagation()Event bubbling up the DOM treeDelete button inside a row — prevent row click from also firing
event.preventDefault()Default browser behaviorAnchor click → use NavigationMixin instead of page reload
event.stopImmediatePropagation()Bubbling + other listeners on same elementComplete isolation of event
handleDeleteClick(event) { event.stopPropagation(); // Parent's onclick will NOT fire — prevents row selection this.deleteRecord(); } handleAnchorClick(event) { event.preventDefault(); // Page will NOT navigate via href // Use NavigationMixin instead this[NavigationMixin.Navigate]({ type: 'standard__recordPage', ... }); } // Can use BOTH together if needed handleSpecialCase(event) { event.stopPropagation(); // Stop bubbling event.preventDefault(); // Cancel browser default }
🌍 Real World Example at XYZ Company

In the Quote Line Items LWC, the delete icon inside each row used stopPropagation() to prevent the row click (which opened a detail panel) from also firing when the delete button was clicked — two separate actions, one event.

🎤 One-Line Answer for Interview
"stopPropagation() prevents the event from bubbling up the DOM so parent handlers don't fire. preventDefault() cancels the browser's default action like form submission or link navigation. They are independent and can be used together."
Q
Question 37 · 🔴 Advanced
Can we create reusable methods in LWC that can be used across multiple components?
✅ YES — Use a Service Component!
Create an LWC service component with only a JS file that exports named utility functions — any component imports and uses them with zero duplication! 🔄
// lwcUtils/lwcUtils.js — Service Component (NO HTML, NO default class) export function formatDate(dateString) { if (!dateString) return ''; return new Date(dateString).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); } export function formatCurrency(amount, currency = 'INR') { return new Intl.NumberFormat('en-IN', { style: 'currency', currency }).format(amount); } export function deepClone(obj) { return JSON.parse(JSON.stringify(obj)); } export function buildErrorMessage(error) { return error?.body?.message || error?.message || 'Unknown error'; } // ───────────────────────────────────────────────────────── // Any component imports and uses these: import { formatDate, formatCurrency, deepClone } from 'c/lwcUtils'; export default class QuoteDetail extends LightningElement { get formattedDate() { return formatDate(this.quoteDate); } get formattedAmount() { return formatCurrency(this.totalAmount); } }
🔑 Key Points for Interviewer
  • 🔥This pattern is called Service Component in Salesforce docs — no HTML, no default class export
  • 💡It's the LWC equivalent of a utility/helper class in Apex
  • 💡Import using named exports: import { fn1, fn2 } from 'c/lwcUtils'
  • 💡No instantiation needed — pure function imports
🎤 One-Line Answer for Interview
"Yes — create a Service Component with only a JS file that exports named utility functions. Any LWC imports these with 'import { fn } from c/serviceName'. This is the LWC equivalent of an Apex utility class — enables true code reuse across components."
Q
Question 38 · 🟠 Intermediate
What is refreshApex() in LWC? When and how do you use it?
✅ Answer
refreshApex() forces a wire adapter to re-fetch fresh data from the server, bypassing the LDS cache — essential after imperative DML to keep wire data in sync! 🔄
import { refreshApex } from '@salesforce/apex'; // Wire Property style — store the wire result @wire(getLineItems, { quoteId: '$recordId' }) wiredLineItems; // ← store as property for refreshApex handleSave() { saveLineItem({ item: this.newItem }) .then(() => { return refreshApex(this.wiredLineItems); // ← pass the wire property }) .then(() => { this.dispatchEvent(new ShowToastEvent({ title: 'Saved', message: 'Line item added', variant: 'success' })); }); } // Wire Function style — must save the full result object wiredResult; // ← save reference here @wire(getLineItems, { quoteId: '$recordId' }) wiredHandler(result) { this.wiredResult = result; // ← save FULL result object if (result.data) this.lineItems = result.data; } handleUpdate() { updateItem({ ... }) .then(() => refreshApex(this.wiredResult)); // ← pass saved reference }
🔑 Key Points for Interviewer
  • 🔥Must store the wire result in a property to use refreshApex — passing this.wiredFunction doesn't work
  • 💡Import from '@salesforce/apex' — not from 'lwc'
  • 💡Common pattern: save (DML) → refreshApex (sync cache) → show toast
  • 💡refreshApex on a non-wire property = no effect
🌍 Real World Example at XYZ Company

In the Quote Line Items LWC, after saveLineItem DML, refreshApex(this.wiredLineItems) was called — the line items list auto-refreshed without any page reload, showing the newly added item immediately.

🎤 One-Line Answer for Interview
"refreshApex() forces a @wire adapter to bypass the LDS cache and re-fetch fresh data from the server — essential after imperative DML. You must store the wire result as a property and pass that property reference to refreshApex()."
Q
Question 39 · 🔴 Advanced
How do you implement Pagination for 20,000 records in LWC?
✅ Answer
Server-side pagination — never load all 20,000 records at once. Use SOQL LIMIT + OFFSET in Apex, fetch only one page at a time, and show page controls in LWC! 📄
// Apex — Server-side pagination @AuraEnabled(cacheable=true) public static Map<String, Object> getRecordsPage( Integer pageSize, Integer pageNumber, String searchKey) { Integer offset = (pageNumber - 1) * pageSize; String search = '%' + searchKey + '%'; List<Account> records = [ SELECT Id, Name, Phone, Type FROM Account WHERE Name LIKE :search ORDER BY Name LIMIT :pageSize OFFSET :offset ]; Integer total = [SELECT COUNT() FROM Account WHERE Name LIKE :search]; return new Map<String, Object>{ 'records' => records, 'totalCount' => total, 'totalPages' => (Integer)Math.ceil((Decimal)total / pageSize) }; } // LWC JS @track records = []; @track currentPage = 1; @track totalPages = 1; pageSize = 50; connectedCallback() { this.loadPage(); } loadPage() { getRecordsPage({ pageSize: this.pageSize, pageNumber: this.currentPage, searchKey: this.searchKey }) .then(data => { this.records = data.records; this.totalPages = data.totalPages; }); } handlePrevious() { if (this.currentPage > 1) { this.currentPage--; this.loadPage(); } } handleNext() { if (this.currentPage < this.totalPages) { this.currentPage++; this.loadPage(); } } get isFirstPage() { return this.currentPage === 1; } get isLastPage() { return this.currentPage >= this.totalPages; }
🔑 Key Points for Interviewer
  • 🔥SOQL OFFSET limit = 2,000 rows max — for datasets beyond that, use cursor-based pagination (last ID query)
  • 💡For better UX on very large sets, lightning-datatable with enable-infinite-loading is cleaner than page buttons
  • 💡Always debounce search in pagination components to prevent excessive Apex calls
  • Never load all 20,000 records — even if SOQL allows it, rendering will crash the browser
🎤 One-Line Answer for Interview
"Implement server-side pagination using SOQL LIMIT/OFFSET in Apex — fetch only one page at a time (e.g., 50 records), return total count for page controls, never load all 20,000 records. Note: SOQL OFFSET has a 2,000 row limit so use cursor-based pagination for very large datasets."
Q
Question 40 · 🔴 Advanced
How do you chain Apex method calls in LWC? What approach for chaining a large number of Apex methods?
✅ Answer
Use async/await for sequential calls and Promise.all() for parallel independent calls. For a large number of chains — redesign to reduce calls using a wrapper Apex method! 🔗
// Way 1 — Sequential (Call B needs data from Call A) async handleLoad() { try { this.account = await getAccount({ accountId: this.recordId }); this.contacts = await getContacts({ accountId: this.account.Id }); this.opps = await getOpportunities({ accountId: this.recordId }); } catch (error) { this.error = error.body.message; } } // Way 2 — Parallel (Independent calls — MUCH faster) async handleLoadAll() { try { const [contacts, opps, cases] = await Promise.all([ getContacts({ accountId: this.recordId }), getOpportunities({ accountId: this.recordId }), getCases({ accountId: this.recordId }) ]); this.contacts = contacts; this.opps = opps; this.cases = cases; } catch (error) { this.error = error.body.message; } } // Way 3 — BEST PRACTICE: Single Apex method with wrapper (for many calls) // ONE call returns everything — zero chaining needed @AuraEnabled(cacheable=true) public static Map<String, Object> getPageData(Id recordId) { return new Map<String, Object>{ 'account' => [SELECT Id, Name FROM Account WHERE Id = :recordId], 'contacts' => [SELECT Id, Name FROM Contact WHERE AccountId = :recordId], 'opps' => [SELECT Id, Name FROM Opportunity WHERE AccountId = :recordId], 'cases' => [SELECT Id, Subject FROM Case WHERE AccountId = :recordId] }; }
📋 When to Use Which Pattern
ScenarioUse
Call B needs data from Call Aasync/await sequential
Calls are independentPromise.all() — parallel, faster
5+ related data needed on loadSingle wrapper Apex method returning Map
20+ chains neededSuggest Research / redesign — architecture problem
🔑 Key Points for Interviewer
  • 🔥The best answer for "large number of chains" is redesign — consolidate into a single wrapper Apex method
  • 💡Promise.all() runs calls in parallel — significantly faster than sequential for independent data
  • 💡Never loop with await inside forEach — use for...of loop for sequential async iteration
  • 💡Single catch() or try/catch covers the entire chain — clean error handling
🎤 One-Line Answer for Interview
"Use async/await for sequential dependent calls and Promise.all() for independent parallel calls. For a large number of Apex chains — the right architectural answer is to consolidate into a single Apex method returning a wrapper Map, eliminating chaining entirely."