Salesforce LWC Interview Questions Part 3 2026 — Accessibility, CDC, Kanban, Virtual Scroll & Anti-Patterns
๐
LWC
⚡ LWC Part 3
Salesforce LWC Interview Questions Part 3 (Q81–Q120)
Accessibility, CDC, Kanban Board, Timeline, Jest Advanced, Virtual Scroll, Anti-Patterns, Agentforce Actions & Real Component Builds
Basic
Intermediate
Advanced
40 Questions · Part 3
Questions Index (Q81–Q120)
81Accessibility & ARIA in LWC82Lightning Locker & LWS Security83Change Data Capture (CDC)84Lightning Flow in LWC85External Libraries (Chart.js, Leaflet)86Star Rating Component87Kanban Board with Drag & Drop88Timeline Component89Data Entry Grid / Editable Table90Progress Tracker / Stepper91All Wire Adapters Reference92URL Params with CurrentPageReference93Aura to LWC Migration94Common LWC Bugs & Fixes95LWC Code Review Checklist96LWC in Salesforce Mobile App97Lightning Out & VF Integration98Newest LWC Features 2025-2699Agentforce Custom Actions100Complete Lifecycle Deep Dive101File Preview Component102Debounce & Throttle Patterns103Toast System for Experience Cloud104Keyboard Navigation in LWC105Optimistic UI Updates106LWC in Visualforce107@salesforce/apex Import Deep Dive108Notification Badge Component109Passing Complex Objects110Real-Time Collaboration111Reusable Form Components112Configurable Dashboard113Race Conditions & Concurrent Calls114Virtual Scroll for Large Data115Reactive Properties Deep Dive116Service Component Pattern117Advanced Jest Testing118Shared Apex State Communication119LWC Anti-Patterns to Avoid120LWC Certification Mastery Guide
Q
Question 81 · ๐ Intermediate
How do you implement Accessibility (ARIA) in LWC?
✅ Answer
Use aria attributes, role, tabindex, and keyboard event handlers — WCAG 2.1 compliance is required for enterprise Salesforce apps. Salesforce Lightning Design System (SLDS) provides accessible patterns! ♿
// HTML — accessible custom component
<template>
<!-- Role and aria-label for custom button -->
<div role="button"
tabindex="0"
aria-label="Delete record"
aria-pressed={isPressed}
onclick={handleClick}
onkeydown={handleKeyDown}
class="custom-btn">
๐️ Delete
</div>
<!-- Live region for dynamic announcements -->
<div aria-live="polite" aria-atomic="true"
class="slds-assistive-text">
{statusMessage}
</div>
<!-- Loading state -->
<template lwc:if={isLoading}>
<div role="status" aria-live="polite">
<lightning-spinner label="Loading..."></lightning-spinner>
</div>
</template>
</template>
// JS — keyboard handler for custom interactive element
handleKeyDown(event) {
// Enter or Space triggers click (standard keyboard behavior)
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.handleClick();
}
// Escape cancels
if (event.key === 'Escape') this.handleClose();
}
| ARIA Attribute | Purpose |
|---|---|
| role="button" | Tells screen reader the element is interactive |
| aria-label | Accessible name for the element (overrides text) |
| aria-live="polite" | Screen reader announces dynamic content changes |
| aria-expanded | Shows whether collapsible content is open/closed |
| aria-disabled | Communicates disabled state to assistive tech |
| tabindex="0" | Makes element keyboard focusable |
๐ Key Points for Interviewer
- ๐ฅCustom interactive elements MUST handle keyboard events — Enter + Space for click, Escape for cancel
- ๐กaria-live="polite" for non-critical updates; aria-live="assertive" for urgent alerts only
- ๐กUse SLDS utility class slds-assistive-text for visually hidden but screen-reader-accessible text
- ๐กAlways test with a screen reader (NVDA, VoiceOver) before releasing enterprise components
๐ค One-Line Answer for Interview
"Accessibility in LWC requires aria roles, aria-label, aria-live for dynamic updates, tabindex for keyboard focus, and keyboard event handlers (Enter/Space/Escape). Always use SLDS accessible patterns and test with screen readers — required for enterprise Salesforce applications."
Q
Question 82 · ๐ด Advanced
What is Lightning Locker and how does it affect LWC development?
✅ Answer
Lightning Locker is Salesforce's security architecture — it isolates component code in secure sandboxes, restricts global DOM access, wraps native APIs, and enforces namespace boundaries. LWC uses a newer, lighter version called Lightning Web Security! ๐
// Lightning Locker / LWS restrictions:
// ❌ Cannot access other namespaces DOM directly
document.querySelectorAll('[data-component-id]'); // Blocked
// ❌ Cannot use eval()
eval('some code'); // Blocked by CSP
// ❌ Cannot access window.__proto__ or modify native prototypes
Array.prototype.myMethod = () => {}; // Blocked
// ❌ Cannot directly access cookies or localStorage in some contexts
// (Salesforce manages session security)
// ✅ Safe alternatives
// Use this.template.querySelector() for DOM access
// Use @salesforce/label, @salesforce/schema for data
// Use LDS (uiRecordApi) for Salesforce data
// Use Platform Events for cross-component communication
// Lightning Web Security (LWS) — newer, lighter
// LWS: same goals as Locker but better performance
// LWS: allows more standard Web APIs than Locker
// LWS: enabled by default in newer orgs
// Check: Setup → Session Settings → Enable Lightning Web Security
| Security Feature | Locker Behavior |
|---|---|
| DOM isolation | Component cannot access another component's DOM |
| Global window | Wrapped — some properties blocked or proxied |
| eval() | Blocked — Content Security Policy enforcement |
| Prototype pollution | Prevented — native prototypes cannot be modified |
| Cross-namespace access | Blocked — each namespace isolated |
| LWS vs Locker | LWS is newer, faster, more standard API support |
๐ Key Points for Interviewer
- ๐ฅLightning Web Security (LWS) is the modern replacement for Lightning Locker — enabled by default in newer orgs
- ๐กIf a third-party library fails: it likely tries to access restricted global APIs — check LWS compatibility
- ๐กStatic resources with third-party libraries: test in sandbox to verify LWS compatibility
- ๐กLWC components interact safely via @api and CustomEvent — Locker enforces this boundary
๐ค One-Line Answer for Interview
"Lightning Locker isolates component code in secure sandboxes — blocking direct DOM cross-access, eval(), and prototype modification. Lightning Web Security (LWS) is the newer lighter replacement. Third-party library failures often indicate LWS incompatibility — check the library's sandbox documentation."
Q
Question 83 · ๐ด Advanced
How do you use Change Data Capture (CDC) in LWC?
✅ Answer
Subscribe to CDC channel with EmpApi — same as Platform Events. CDC publishes record change events (create/update/delete) in real time. Channel name format: /data/ObjectName__ChangeEvent! ๐
import { subscribe, unsubscribe, onError }
from 'lightning/empApi';
export default class CdcListener extends LightningElement {
subscription = null;
// CDC channel — /data/ObjectName__ChangeEvent
channelName = '/data/Account__ChangeEvent';
// Standard objects: /data/AccountChangeEvent (no __)
connectedCallback() {
this.subscribeToCdc();
onError(err => console.error('CDC error:', err));
}
subscribeToCdc() {
subscribe(this.channelName, -1, (message) => {
const changeType = message.data.payload.ChangeEventHeader.changeType;
const recordIds = message.data.payload.ChangeEventHeader.recordIds;
const changedFields = message.data.payload.ChangeEventHeader.changedFields;
if (changeType === 'UPDATE') {
this.handleUpdate(recordIds, changedFields);
} else if (changeType === 'CREATE') {
this.handleCreate(recordIds);
} else if (changeType === 'DELETE') {
this.handleDelete(recordIds);
}
}).then(sub => this.subscription = sub);
}
handleUpdate(ids, fields) {
// Refresh wire cache for changed records
if (ids.includes(this.recordId)) {
return refreshApex(this.wiredRecord);
}
}
disconnectedCallback() {
if (this.subscription) unsubscribe(this.subscription, () => {});
}
}
๐ Key Points for Interviewer
- ๐ฅCDC channel naming: Custom objects = /data/MyObject__ChangeEvent; Standard = /data/AccountChangeEvent
- ๐กChangeEventHeader contains: changeType (CREATE/UPDATE/DELETE/UNDELETE), recordIds, changedFields
- ๐กCDC requires: Enable Change Data Capture in Setup → Integrations → Change Data Capture
- ๐กUse refreshApex() when a CDC event matches the current record to sync wire cache
๐ค One-Line Answer for Interview
"CDC in LWC uses EmpApi subscribe with channel /data/ObjectName__ChangeEvent — same API as Platform Events. ChangeEventHeader contains changeType and recordIds. Call refreshApex() when the changed record matches the component's current record to keep LDS cache in sync."
Q
Question 84 · ๐ Intermediate
How do you embed a Lightning Flow in LWC?
✅ Answer
Use the lightning-flow component — pass the flow API name, input variables, and handle onstatuschange for completion! ๐
// HTML
<template>
<template lwc:if={showFlow}>
<lightning-flow
flow-api-name="Create_Opportunity_Flow"
flow-input-variables={inputVariables}
onstatuschange={handleStatusChange}>
</lightning-flow>
</template>
<template lwc:else>
<lightning-button label="Start Flow" onclick={handleStart}>
</lightning-button>
</template>
</template>
// JS
@track showFlow = false;
get inputVariables() {
return [
{ name: 'recordId', type: 'String', value: this.recordId },
{ name: 'accountName', type: 'String', value: this.accountName }
];
}
handleStart() {
this.showFlow = true;
}
handleStatusChange(event) {
const status = event.detail.status;
if (status === 'FINISHED' || status === 'FINISHED_SCREEN') {
this.showFlow = false; // hide flow
this.dispatchEvent(new ShowToastEvent({
title: 'Success', message: 'Flow completed!', variant: 'success'
}));
return refreshApex(this.wiredData); // refresh data
}
if (status === 'ERROR') {
console.error('Flow error:', event.detail.errors);
}
}
| Status Value | Meaning |
|---|---|
| STARTED | Flow has started |
| PAUSED | User paused the flow (waits for resume) |
| FINISHED | Screen flow finished all screens |
| FINISHED_SCREEN | Last screen completed |
| ERROR | Flow encountered an error |
๐ Key Points for Interviewer
- ๐ฅflowInputVariables: array of {name, type, value} — type must be String, Number, Boolean, or SObject
- ๐กstatus === FINISHED_SCREEN: use for screen flows; FINISHED: for auto-launched flows
- ๐กCannot access flow output variables from LWC directly — use a Platform Event or Custom Event from the flow
- ๐กlightning-flow respects all existing flow screens — reuse existing flows in LWC without rebuilding
๐ค One-Line Answer for Interview
"Use lightning-flow with flow-api-name and flow-input-variables to embed a Flow. Handle onstatuschange — FINISHED or FINISHED_SCREEN indicates completion. input variables must be typed as String, Number, Boolean, or SObject arrays."
Q
Question 85 · ๐ด Advanced
How do you use external JavaScript libraries (Chart.js, Leaflet.js, D3.js) in LWC?
✅ Answer
Upload as Static Resource → import URL with @salesforce/resourceUrl → loadScript in renderedCallback with boolean guard → initialize library in .then() callback! ๐
// Chart.js example
import { LightningElement, wire } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import CHARTJS from '@salesforce/resourceUrl/ChartJS';
export default class ChartComponent extends LightningElement {
chart;
chartJsInitialized = false;
renderedCallback() {
if (this.chartJsInitialized) return;
this.chartJsInitialized = true;
loadScript(this, CHARTJS)
.then(() => {
this.initializeChart();
})
.catch(error => {
console.error('Chart.js load failed:', error);
});
}
initializeChart() {
// Canvas must be accessed AFTER library loads
const canvas = this.template.querySelector('canvas');
const ctx = canvas.getContext('2d');
this.chart = new Chart(ctx, {
type: 'bar',
data: {
labels: this.labels,
datasets: [{
label: 'Revenue',
data: this.data,
backgroundColor: '#7C3AED'
}]
},
options: { responsive: true }
});
}
// Update chart when data changes
updateChart() {
if (this.chart) {
this.chart.data.datasets[0].data = this.newData;
this.chart.update();
}
}
disconnectedCallback() {
if (this.chart) this.chart.destroy(); // prevent memory leak
}
}
๐ Key Points for Interviewer
- ๐ฅAlways use boolean guard in renderedCallback — prevent re-loading on every render
- ๐กAccess canvas AFTER loadScript .then() — library must be available before initialization
- ๐กDestroy chart in disconnectedCallback — prevents memory leaks
- ๐กLWS compatibility: some libraries use restricted APIs — test in sandbox before production
๐ค One-Line Answer for Interview
"Upload library as Static Resource, import URL with @salesforce/resourceUrl, use loadScript in renderedCallback with a boolean guard, then initialize in .then(). Access DOM elements after library loads. Always destroy library instances in disconnectedCallback to prevent memory leaks."
Q
Question 86 · ๐ด Advanced
How do you build a star rating component in LWC?
✅ Answer
Use for:each to render 5 stars, track hovered/selected rating, dispatch CustomEvent on selection. Use CSS :hover and conditional classes for visual feedback! ⭐
// starRating.js
export default class StarRating extends LightningElement {
@api value = 0; // selected rating (from parent)
@api maxStars = 5;
@api readOnly = false;
hoveredRating = 0;
get stars() {
return Array.from({ length: this.maxStars }, (_, i) => ({
index: i + 1,
isFilled: (this.hoveredRating || this.value) >= (i + 1),
key: i + 1
}));
}
handleStarHover(event) {
if (this.readOnly) return;
this.hoveredRating = parseInt(event.currentTarget.dataset.index);
}
handleMouseLeave() {
this.hoveredRating = 0; // reset on mouse leave
}
handleStarClick(event) {
if (this.readOnly) return;
const rating = parseInt(event.currentTarget.dataset.index);
this.dispatchEvent(new CustomEvent('ratingchange', {
detail: { rating }
}));
}
}
// HTML
// <template for:each={stars} for:item="star">
// <span key={star.key}
// data-index={star.index}
// class={starClass}
// onmouseenter={handleStarHover}
// onclick={handleStarClick}>
// {star.isFilled ? '★' : '☆'}
// </span>
// </template>
// Parent usage
// <c-star-rating value={currentRating} onratingchange={handleRating}>
๐ Key Points for Interviewer
- ๐ฅUse hoveredRating || this.value to show preview on hover, fall back to selected value
- ๐กdata-index attribute on each star passes the star number to event handlers
- ๐กreadOnly @api allows the same component to display ratings without interaction
- ๐กFire CustomEvent with rating detail — never mutate @api value directly in child
๐ค One-Line Answer for Interview
"Star rating uses for:each to render stars, tracks hoveredRating for preview and value for selection. On hover show preview (hoveredRating), on click fire ratingchange CustomEvent. On mouse leave reset hoveredRating to 0."
Q
Question 87 · ๐ด Advanced
How do you build a Kanban board in LWC?
✅ Answer
Kanban board: columns from picklist values, cards from records, drag-and-drop via HTML5 Drag and Drop API, column change fires Apex DML! ๐️
// kanbanBoard.js — simplified key patterns
export default class KanbanBoard extends LightningElement {
@api objectApiName = 'Opportunity';
@api groupByField = 'StageName';
columns = [];
draggedCardId = null;
// HTML5 Drag and Drop
handleDragStart(event) {
this.draggedCardId = event.currentTarget.dataset.id;
event.dataTransfer.effectAllowed = 'move';
event.currentTarget.classList.add('dragging');
}
handleDragOver(event) {
event.preventDefault(); // allow drop
event.dataTransfer.dropEffect = 'move';
event.currentTarget.classList.add('drag-over');
}
handleDragLeave(event) {
event.currentTarget.classList.remove('drag-over');
}
async handleDrop(event) {
event.preventDefault();
const targetColumn = event.currentTarget.dataset.column;
event.currentTarget.classList.remove('drag-over');
if (!this.draggedCardId || !targetColumn) return;
try {
// Update record via Apex
await updateRecordField({
recordId: this.draggedCardId,
fieldName: this.groupByField,
fieldValue: targetColumn
});
await refreshApex(this.wiredRecords);
this.draggedCardId = null;
} catch (error) {
this.showToast('Error', error.body.message, 'error');
}
}
handleDragEnd(event) {
event.currentTarget.classList.remove('dragging');
}
}
๐ Key Points for Interviewer
- ๐ฅevent.preventDefault() in handleDragOver is mandatory to enable dropping
- ๐กdataTransfer.effectAllowed/dropEffect provides visual cursor feedback during drag
- ๐กdata-id on cards and data-column on columns pass identifiers to event handlers
- ๐กOptimistic UI: update local state immediately, revert on Apex failure for better UX
๐ค One-Line Answer for Interview
"Kanban board uses HTML5 Drag and Drop API — handleDragStart stores the card ID, handleDragOver prevents default to enable drop, handleDrop calls Apex to update the record's groupBy field and refreshApex to sync. data-id and data-column attributes pass context to handlers."
Q
Question 88 · ๐ด Advanced
How do you build a timeline component in LWC?
✅ Answer
Timeline renders a sorted list of events with date grouping, CSS vertical line, and icon-based event types. Reusable via @api events array! ⏱️
// timeline.js
export default class Timeline extends LightningElement {
@api events = [];
@api title = 'Activity Timeline';
get sortedGroups() {
// Sort events by date descending
const sorted = [...this.events].sort(
(a, b) => new Date(b.date) - new Date(a.date)
);
// Group by date
const groups = {};
sorted.forEach(event => {
const dateKey = new Date(event.date).toLocaleDateString('en-IN', {
day: '2-digit', month: 'short', year: 'numeric'
});
if (!groups[dateKey]) groups[dateKey] = [];
groups[dateKey].push(event);
});
return Object.entries(groups).map(([date, items]) => ({
date,
items,
key: date
}));
}
get iconMap() {
return {
call: 'utility:call',
email: 'utility:email',
meeting: 'utility:event',
task: 'utility:task',
note: 'utility:note'
};
}
getIcon(type) {
return this.iconMap[type] || 'utility:record';
}
}
// CSS — timeline vertical line
// .timeline-item { position:relative; padding-left:40px; }
// .timeline-item::before { content:''; position:absolute;
// left:14px; top:0; bottom:-20px;
// width:2px; background:#EDE9FE; }
// .timeline-dot { position:absolute; left:5px; top:4px;
// width:18px; height:18px; border-radius:50%;
// background:#7C3AED; }
๐ Key Points for Interviewer
- ๐ฅGroup events by date using a reducer — Object.entries converts to array for for:each
- ๐ก[...this.events].sort() — spread first to avoid mutating the @api array
- ๐กCSS ::before pseudo-element creates the vertical timeline line
- ๐กkey on grouped items must be unique — use date string as key
๐ค One-Line Answer for Interview
"Timeline component groups sorted events by date using reduce, renders with CSS vertical line via ::before pseudo-element, and maps event types to SLDS icons. Always spread @api arrays before sorting to avoid mutating parent data."
Q
Question 89 · ๐ด Advanced
How do you implement a data entry table (editable grid) in LWC?
✅ Answer
Custom editable grid: array of row objects with editable fields, track changes per cell, validate on blur, submit all rows via Apex! ๐
export default class DataEntryGrid extends LightningElement {
@track rows = [
{ id: 1, name: '', qty: 0, price: 0, isValid: true },
{ id: 2, name: '', qty: 0, price: 0, isValid: true }
];
get rowsWithTotal() {
return this.rows.map(row => ({
...row,
total: row.qty * row.price
}));
}
get grandTotal() {
return this.rows.reduce((sum, row) => sum + (row.qty * row.price), 0);
}
handleCellChange(event) {
const rowId = parseInt(event.currentTarget.dataset.rowId);
const field = event.currentTarget.dataset.field;
const value = event.target.value;
this.rows = this.rows.map(row =>
row.id === rowId ? { ...row, [field]: value } : row
);
}
handleAddRow() {
const newId = this.rows.length + 1;
this.rows = [
...this.rows,
{ id: newId, name: '', qty: 0, price: 0, isValid: true }
];
}
handleDeleteRow(event) {
const rowId = parseInt(event.currentTarget.dataset.rowId);
this.rows = this.rows.filter(row => row.id !== rowId);
}
async handleSave() {
const isValid = this.validateAllRows();
if (!isValid) return;
await saveLineItems({ items: this.rows });
this.dispatchEvent(new CustomEvent('saved'));
}
}
๐ Key Points for Interviewer
- ๐ฅUse immutable update pattern — this.rows.map() creates new array reference = reactive re-render
- ๐กdata-row-id and data-field attributes on inputs pass context without class state
- ๐กgrandTotal getter auto-recalculates on every render — no need to track separately
- ๐กValidate on save: check all rows, set isValid flag, highlight invalid rows in template
๐ค One-Line Answer for Interview
"Custom editable grid tracks rows as @track array. Cell changes use immutable map pattern (this.rows.map with spread) to update specific row. data-row-id and data-field attributes pass context to the shared handler. grandTotal getter recalculates automatically."
Q
Question 90 · ๐ด Advanced
How do you implement a progress tracker / step indicator in LWC?
✅ Answer
Use lightning-progress-indicator for standard steps, or custom CSS circles with connector lines for fully branded step tracker! ✅
// Using lightning-progress-indicator (easiest)
<lightning-progress-indicator
current-step={currentStepLabel}
type="base"
has-error={hasError}>
<lightning-progress-step
label="Application"
value="step1">
</lightning-progress-step>
<lightning-progress-step
label="Review"
value="step2">
</lightning-progress-step>
<lightning-progress-step
label="Approval"
value="step3">
</lightning-progress-step>
</lightning-progress-indicator>
// Custom progress tracker
get progressSteps() {
return this.steps.map((step, index) => ({
...step,
isCompleted: index < this.currentIndex,
isActive: index === this.currentIndex,
isPending: index > this.currentIndex,
statusClass: index < this.currentIndex ? 'completed'
: index === this.currentIndex ? 'active' : 'pending',
key: step.value
}));
}
// CSS
// .step-circle { width:32px;height:32px;border-radius:50%; }
// .completed { background:#10B981; }
// .active { background:#7C3AED; }
// .pending { background:#E5E7EB; }
// .connector { flex:1;height:2px; }
๐ Key Points for Interviewer
- ๐ฅlightning-progress-indicator current-step must exactly match the value of a lightning-progress-step
- ๐กhas-error flag shows error state on the current step
- ๐กFor custom: derive isCompleted, isActive, isPending from index comparison in getter
- ๐กConnector line between steps: flex container with flex:1 div for auto-sizing
๐ค One-Line Answer for Interview
"lightning-progress-indicator with current-step matching step value provides instant step tracking. For custom: use a getter mapping step index to isCompleted/isActive/isPending status classes and CSS circle with connector line between steps."
Q
Question 91 · ๐ Intermediate
What are all the important wire adapters in LWC? Complete reference.
✅ Answer
LWC has 15+ wire adapters covering records, objects, picklists, users, nav items, related lists, and more — all part of LDS, zero Apex! ๐
| Wire Adapter | Import | What It Returns |
|---|---|---|
| getRecord | lightning/uiRecordApi | Single record with fields |
| getRecords | lightning/uiRecordApi | Multiple records in one call |
| getObjectInfo | lightning/uiObjectInfoApi | Object metadata, fields, record types |
| getPicklistValues | lightning/uiObjectInfoApi | Picklist field values for record type |
| getPicklistValuesByRecordType | lightning/uiObjectInfoApi | All picklist values grouped by record type |
| getRelatedListRecords | lightning/uiRelatedListApi | Related list records |
| getRelatedListInfo | lightning/uiRelatedListApi | Related list metadata |
| getNavItems | lightning/uiNavigationApi | App navigation items |
| CurrentPageReference | lightning/navigation | Current page URL and state |
| MessageContext | lightning/messageService | LMS context for publish/subscribe |
| graphql | lightning/uiGraphQLApi | GraphQL query result |
// Most useful advanced adapters
import { CurrentPageReference } from 'lightning/navigation';
import { getNavItems } from 'lightning/uiNavigationApi';
import { getRecords } from 'lightning/uiRecordApi';
// CurrentPageReference — get URL params
@wire(CurrentPageReference)
pageRef;
get recordId() {
return this.pageRef?.state?.recordId;
}
// getRecords — fetch multiple records at once
@wire(getRecords, {
records: [
{ recordIds: ['001xxx', '001yyy'], fields: ['Account.Name'] }
]
})
wiredRecords;
๐ Key Points for Interviewer
- ๐ฅCurrentPageReference gives URL state — use to read page params without @api recordId
- ๐กgetRecords (plural): fetch multiple records in ONE wire call — more efficient than multiple getRecord wires
- ๐กAll LDS wire adapters respect FLS and sharing — no manual permission checks needed
- ๐กgetPicklistValuesByRecordType: gets ALL picklist values for all fields grouped by record type
๐ค One-Line Answer for Interview
"LWC has 15+ wire adapters for records (getRecord/getRecords), metadata (getObjectInfo/getPicklistValues), related lists (getRelatedListRecords), navigation (CurrentPageReference/getNavItems), and GraphQL. All respect FLS and sharing automatically."
Q
Question 92 · ๐ Intermediate
How do you read URL parameters in LWC using CurrentPageReference?
✅ Answer
Wire CurrentPageReference from lightning/navigation — gives access to current page type, attributes (recordId, objectApiName), and state (custom URL params)! ๐
import { LightningElement, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
export default class UrlParamsReader extends LightningElement {
@wire(CurrentPageReference)
pageRef;
// Record page: recordId is in attributes
get recordId() {
return this.pageRef?.attributes?.recordId;
}
// Object API name from page
get objectApiName() {
return this.pageRef?.attributes?.objectApiName;
}
// Custom state params (from NavigationMixin + state object)
get customParam() {
return this.pageRef?.state?.c__myParam; // c__ prefix for custom
}
// Navigate with custom state
navigateWithParam() {
this[NavigationMixin.Navigate]({
type: 'standard__objectPage',
attributes: { objectApiName: 'Opportunity', actionName: 'list' },
state: {
c__filterType: 'active', // c__ prefix required
c__region: 'Mumbai'
}
});
}
}
๐ Key Points for Interviewer
- ๐ฅState parameters must be prefixed with c__ (namespace) to avoid conflicts
- ๐กpageRef.type: page type (standard__recordPage, standard__objectPage, etc.)
- ๐กpageRef.attributes: page-specific attributes (recordId, objectApiName, actionName)
- ๐กpageRef.state: custom query parameters — use for filtering, tab selection, etc.
๐ค One-Line Answer for Interview
"CurrentPageReference wire adapter gives access to current page type, attributes (recordId, objectApiName), and state (custom URL params with c__ prefix). Use NavigationMixin.Navigate with state object to set custom params, and read them back with pageRef.state."
Q
Question 93 · ๐ Intermediate
How do you migrate from Aura to LWC? What is the migration strategy?
✅ Answer
Migration strategy: identify Aura-only dependencies → rebuild LWC equivalent → replace Aura in page → coexist during transition. LWC can be embedded inside Aura but not vice versa! ๐
| Aura Concept | LWC Equivalent |
|---|---|
| Component Events | CustomEvent with bubbles/composed |
| Application Events | Lightning Message Service (LMS) |
| force:createRecord | NavigationMixin + standard__objectPage |
| force:editRecord | NavigationMixin + actionName:edit |
| force:navigateToRecord | NavigationMixin + standard__recordPage |
| {!v.property} | Direct property binding {property} |
| {!c.actionHandler} | {actionHandler} — no c. prefix |
| helper.js | Methods directly in the component class |
| @api or @track properties | |
| $A.enqueueAction | Imperative Apex call |
// Aura component
<aura:component>
<aura:attribute name="recordId" type="Id"/>
<aura:attribute name="accounts" type="List"/>
<lightning:button label="{!v.buttonLabel}"
onclick="{!c.handleClick}"/>
</aura:component>
// LWC equivalent
export default class MyLwcComp extends LightningElement {
@api recordId;
@track accounts = [];
@api buttonLabel;
handleClick() { /* same logic */ }
}
// HTML: <lightning-button label={buttonLabel}
// onclick={handleClick}></lightning-button>
๐ Key Points for Interviewer
- ๐ฅLWC CAN be embedded inside Aura — use as migration bridge
- ๐กAura helper.js methods → move to class methods in LWC JS file
- ๐ก$A.util.addClass/removeClass → template.querySelector + classList.add/remove in LWC
- ๐กPriority: migrate components with most users first; Aura components can remain alongside LWC
๐ค One-Line Answer for Interview
"Aura to LWC migration: replace aura:attribute with @api/@track, {!c.handler} with {handler}, Application Events with LMS, Component Events with CustomEvent. LWC can be embedded inside Aura as a migration bridge. Migrate high-usage components first."
Q
Question 94 · ๐ด Advanced
What are the most common LWC bugs and how do you fix them?
✅ Answer
Top LWC bugs: null from querySelector (timing), infinite renderedCallback, wire not firing (undefined param), template mutation of @api, and stale wire cache after DML. ๐
| Bug | Root Cause | Fix |
|---|---|---|
| querySelector returns null | Called in connectedCallback/constructor before DOM renders | Move to renderedCallback or event handler |
| renderedCallback infinite loop | Modifying tracked property inside renderedCallback without guard | Add boolean flag: if(this.initialized) return |
| Wire never fires | Reactive parameter is undefined | Check @api receives value; ensure $ prefix on reactive params |
| Cannot mutate @api property | Child cannot modify parent-owned data | Dispatch CustomEvent with new value; parent updates and passes back |
| Stale data after DML | LDS cache not refreshed | Call refreshApex(this.wiredResult) after imperative DML |
| CustomEvent not caught | Event name mismatch or missing bubbles+composed | Check on+eventname in HTML; add bubbles:true+composed:true if cross-shadow |
// Bug 1: null querySelector — WRONG
connectedCallback() {
this.template.querySelector('.my-input'); // null ❌
}
// Fix: use renderedCallback
renderedCallback() {
const input = this.template.querySelector('.my-input'); // ✅
}
// Bug 2: renderedCallback infinite loop
renderedCallback() {
this.myTrackedProp = 'new value'; // ❌ causes re-render → loop!
}
// Fix: boolean guard
if (this.initialized) return;
this.initialized = true;
this.myTrackedProp = 'new value'; // ✅ runs once
// Bug 3: wire not firing
@api recordId; // received from parent? check!
@wire(getRecord, { recordId: '$recordId' }) // $ makes it reactive
wiredRecord; // fires only when recordId has a truthy value
๐ Key Points for Interviewer
- ๐ฅInfinite renderedCallback loop is the #1 LWC bug in senior interviews — always know the guard pattern
- ๐กWire not firing: check if the reactive param is undefined — wire skips when param is undefined/null
- ๐กStale LDS cache: after any Apex DML, call refreshApex() to force wire re-fetch
- ๐กTemplate binding: {} bindings are read-only — never try to two-way bind a complex expression
๐ค One-Line Answer for Interview
"Top LWC bugs: querySelector in connectedCallback (null — move to renderedCallback), renderedCallback infinite loop (add boolean guard), wire not firing (undefined reactive param), @api mutation (dispatch event instead), stale cache (call refreshApex after DML)."
Q
Question 95 · ๐ Intermediate
What are the LWC best practices checklist for code reviews?
✅ Answer
LWC code review checklist covers performance, security, accessibility, error handling, cleanup, and testing — 20 essential checks! ✅
/* LWC Code Review Checklist */
// ✅ PERFORMANCE
// [ ] Getters are memoized if computationally expensive
// [ ] renderedCallback has a boolean guard
// [ ] loadScript has a boolean guard
// [ ] No DOM queries in loops (cache querySelector results)
// [ ] Server-side pagination for datasets >500 records
// [ ] Promise.all() for independent Apex calls
// ✅ CORRECTNESS
// [ ] Wire result stored for refreshApex usage
// [ ] Both data AND error handled in wire function
// [ ] extractError utility handles Array and Object shapes
// [ ] draftValues cleared after lightning-datatable inline save
// [ ] @api arrays spread before sorting (avoid mutation)
// ✅ CLEANUP
// [ ] LMS unsubscribed in disconnectedCallback
// [ ] EmpApi unsubscribed in disconnectedCallback
// [ ] IntersectionObserver disconnected in disconnectedCallback
// [ ] External library (Chart.js, Leaflet) destroyed in disconnectedCallback
// ✅ SECURITY & ACCESSIBILITY
// [ ] No eval() or prototype mutation
// [ ] All interactive elements keyboard accessible
// [ ] aria-label on icon-only buttons
// [ ] Form inputs have associated labels
// ✅ TESTING
// [ ] Jest tests cover wire success and error paths
// [ ] Jest tests simulate user interactions
// [ ] 80%+ code coverage
๐ Key Points for Interviewer
- ๐ฅThe most missed item: disconnectedCallback cleanup for subscriptions and observers
- ๐กSpread before sort: always [...this.apiArray].sort() — never mutate @api data directly
- ๐กWire error path: always handle error alongside data — not just the happy path
- ๐กShare this checklist in code reviews — reduces 80% of common LWC bugs
๐ค One-Line Answer for Interview
"LWC best practices checklist: memoize expensive getters, boolean guards in renderedCallback and loadScript, handle wire errors, clear draftValues after save, spread @api arrays before sorting, unsubscribe all subscriptions in disconnectedCallback, and ensure keyboard accessibility."
Q
Question 96 · ๐ด Advanced
How does LWC handle the Salesforce Mobile App?
✅ Answer
LWC works in Salesforce Mobile App by targeting lightning__RecordPage and supporting mobile-specific features. Key: responsive CSS, lightning-card, and avoiding desktop-only APIs! ๐ฑ
// .js-meta.xml — include mobile targets
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__MobilePage</target> <!-- mobile-specific -->
</targets>
// Detect mobile in JS
get isMobile() {
return window.matchMedia('(max-width: 768px)').matches
|| navigator.userAgent.toLowerCase().includes('mobile');
}
// Responsive CSS
@media (max-width: 768px) {
.desktop-only { display: none; }
.data-grid { grid-template-columns: 1fr; } /* single column on mobile */
}
// Mobile-friendly interaction patterns:
// ✅ Large touch targets (min 44x44px — Apple HIG guideline)
// ✅ lightning-button-icon for compact actions
// ✅ Avoid hover-dependent interactions (no hover on touch)
// ✅ lightning-card for native-feel containers
// ✅ Minimize data shown on first load (lazy load details)
| Feature | Mobile Support |
|---|---|
| lightning-record-form | ✅ Fully responsive |
| lightning-datatable | ⚠️ Works but complex on small screen |
| Custom Modals | ✅ Works — ensure mobile-friendly sizing |
| Drag and Drop | ❌ Not supported on touch |
| FSL Mobile App | Separate app — FSL-specific components only |
๐ Key Points for Interviewer
- ๐ฅTouch devices: no hover events — never rely on hover for critical functionality
- ๐กMin touch target size: 44x44px (Apple HIG) / 48x48px (Material Design)
- ๐กSalesforce Mobile App uses WebView — same LWC runtime, different viewport
- ๐กAvoid horizontal scrolling — use single-column layouts on mobile
๐ค One-Line Answer for Interview
"LWC in Salesforce Mobile App requires responsive CSS, min 44px touch targets, no hover-dependent interactions, and avoiding drag-and-drop. Use @media queries for mobile layouts and lightning-card for native-feel containers. lightning__MobilePage target for mobile-specific pages."
Q
Question 97 · ๐ Intermediate
What is Lightning Out and how does LWC work outside Salesforce?
✅ Answer
Lightning Out embeds Aura components in external pages (Visualforce, third-party sites). For LWC in external contexts: Experience Cloud sites or using the Salesforce APIs. Pure LWC cannot run standalone outside Salesforce yet! ๐
// Lightning Out — embeds Aura components in external sites
// 1. Create an Aura dependency app
<aura:application extends="ltng:outApp" access="GLOBAL">
<aura:dependency resource="c:myAuraComponent"/>
</aura:application>
// 2. External page JavaScript
$Lightning.use("c:MyApp", function() {
$Lightning.createComponent("c:myAuraComponent",
{ recordId: '001xxx' },
"containerId",
function(cmp) { console.log('Component created', cmp); }
);
});
// LWC alternatives for external usage:
// ✅ Experience Cloud — LWC runs on Salesforce-hosted community sites
// ✅ Mobile SDK — Salesforce Mobile SDK for native mobile apps
// ✅ LWC OSS — Open Source version of LWC for standalone apps
// (different from Salesforce LWC — no @salesforce/* imports)
// LWC OSS (lwc.dev) — runs without Salesforce
import { LightningElement, track } from 'lwc';
// Standard LWC without @salesforce/* imports works in LWC OSS
๐ Key Points for Interviewer
- ๐ฅLightning Out is for Aura components only — LWC cannot use Lightning Out directly
- ๐กLWC OSS (lwc.dev): open-source version of LWC for standalone web apps — no @salesforce/* imports
- ๐กFor Salesforce LWC outside Lightning: use Experience Cloud (hosted by Salesforce)
- ๐กSalesforce Mobile SDK: build native iOS/Android apps that embed Salesforce LWC components
๐ค One-Line Answer for Interview
"Lightning Out embeds Aura components in external sites — LWC cannot use Lightning Out directly. For LWC outside Salesforce: use Experience Cloud sites or LWC OSS (open-source version) for standalone web apps. LWC OSS uses the same syntax but no @salesforce/* imports."
Q
Question 98 · ๐ด Advanced
What are the newest LWC features in 2025-2026?
✅ Answer
Latest LWC features: lwc:ref (Summer '23), lwc:component for dynamic rendering, GraphQL wire adapter, Light DOM, Reactive Properties improvements, and Agentforce LWC integration! ๐
| Feature | API Version | Key Benefit |
|---|---|---|
| lwc:ref + this.refs | API 59.0+ (Summer '23) | Direct element access without selector strings |
| lwc:component + lwc:is | API 59.0+ | Dynamic component rendering at runtime |
| GraphQL wire adapter | API 59.0+ | Multi-object queries in one wire call |
| Light DOM (renderMode) | API 59.0+ | Remove shadow encapsulation for global CSS |
| Dynamic Expressions in keys | API 60.0+ | More flexible iteration patterns |
| Agentforce LWC actions | API 61.0+ | LWC as custom actions in Agentforce agents |
| Enhanced LWC in Flow | API 61.0+ | Better LWC screen component for Flows |
| LWC Signals (preview) | Upcoming | Fine-grained reactivity without re-renders |
// lwc:ref — Summer '23+ (API 59.0)
// HTML: <input lwc:ref="nameInput" />
this.refs.nameInput.focus();
// Dynamic components — lwc:component
// HTML: <lwc:component lwc:is={constructor}></lwc:component>
this.dynamicConstructor = await load('c/myComp');
// Agentforce — LWC as agent action (API 61.0)
// .js-meta.xml target: lightning__AgentAction
// Component appears as an action option in Agentforce agent configuration
// LWC Signals (upcoming)
// import { signal, computed } from '@lwc/signals';
// const count = signal(0);
// const doubled = computed(() => count.value * 2);
๐ Key Points for Interviewer
- ๐ฅKnow the API version for each feature — interviewers ask "when was this introduced?"
- ๐กlwc:ref is the most commonly asked new feature in 2025-26 interviews
- ๐กGraphQL wire adapter: reduces round-trips for related data significantly
- ๐กLWC Signals: upcoming reactive system inspired by SolidJS — watch for this in 2026 interviews
๐ค One-Line Answer for Interview
"2025-26 LWC additions: lwc:ref for direct element access (API 59), lwc:component for dynamic rendering (API 59), GraphQL wire adapter for multi-object queries (API 59), Light DOM for global CSS (API 59), and Agentforce LWC agent actions (API 61)."
Q
Question 99 · ๐ด Advanced
How do LWC components work as Agentforce custom actions?
✅ Answer
LWC components can serve as custom actions in Agentforce agents by targeting lightning__AgentAction in metadata. The agent renders the LWC as a UI for complex data collection or confirmation! ๐ค
// .js-meta.xml — target for Agentforce
<LightningComponentBundle>
<isExposed>true</isExposed>
<targets>
<target>lightning__AgentAction</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AgentAction">
<!-- Input params from agent -->
<property name="accountId" type="String"
label="Account ID"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
// JS — agent action component
export default class AgentActionComp extends LightningElement {
@api accountId; // received from agent context
@api invocationContext; // agent invocation details
// Complete the action and return output to agent
handleComplete() {
// Use standard output event
const outputEvent = new CustomEvent('agentactioncomplete', {
detail: {
status: 'SUCCESS',
outputValues: { message: 'Done', recordId: this.newRecordId }
}
});
this.dispatchEvent(outputEvent);
}
}
๐ Key Points for Interviewer
- ๐ฅlightning__AgentAction target makes LWC appear as a selectable action in Agentforce agent builder
- ๐กAgent passes context via @api properties — accountId, recordId, or custom inputs
- ๐กComplete the action by firing the standard agentactioncomplete CustomEvent
- ๐กAgentforce actions can collect complex data from users that the AI agent cannot gather alone
๐ค One-Line Answer for Interview
"LWC Agentforce actions target lightning__AgentAction in metadata — the component receives context via @api properties and signals completion by firing the agentactioncomplete CustomEvent with output values. Use for complex data collection the AI agent cannot handle through conversation alone."
Q
Question 100 · ๐ด Advanced
What is the LWC component lifecycle in complete detail — all phases?
✅ Answer
Complete LWC lifecycle: Module evaluation → constructor → wire → connectedCallback → render → renderedCallback → repeat on updates → disconnectedCallback on removal! ๐
// Complete lifecycle order:
// 1. MODULE EVALUATION
// Static imports resolved, class defined
// 2. CONSTRUCTOR — component instantiated
constructor() {
super(); // MANDATORY — always first
// ✅ Initialize local properties
// ❌ No DOM access — shadow root not attached
// ❌ No child access — children not created
// ❌ No @api access — parent hasn't set values
}
// 3. @WIRE — fires BEFORE connectedCallback
@wire(getRecord, { recordId: '$recordId' })
wiredRecord; // framework fetches data
// 4. CONNECTED CALLBACK — inserted into DOM
connectedCallback() {
// ✅ DOM exists — but shadow content not rendered yet
// ✅ @api values are available
// ✅ Subscribe to LMS channels
// ❌ Cannot query shadow DOM (not rendered yet)
}
// 5. RENDER — framework creates shadow DOM
// render() can return custom template here
// 6. RENDERED CALLBACK — after every render/re-render
renderedCallback() {
if (this.initialized) return; // guard!
this.initialized = true;
// ✅ DOM fully rendered — can query
// ✅ loadScript here
// ⚠️ Modifying @track here = re-render = loop!
}
// 7. DISCONNECTED CALLBACK — removed from DOM
disconnectedCallback() {
// ✅ Clean up: unsubscribe, disconnect observers, destroy libraries
unsubscribe(this.subscription);
if (this.chart) this.chart.destroy();
}
| Lifecycle Phase | DOM Available? | @api Available? | Can Query Shadow? |
|---|---|---|---|
| constructor() | ❌ No | ❌ No | ❌ No |
| @wire | ❌ No | Partially | ❌ No |
| connectedCallback() | ✅ Yes | ✅ Yes | ❌ Not yet rendered |
| renderedCallback() | ✅ Yes | ✅ Yes | ✅ Yes |
| disconnectedCallback() | Detached | ✅ Yes | ❌ Removed |
๐ Key Points for Interviewer
- ๐ฅWire fires before connectedCallback — critical for interview questions about order
- ๐กconstructor: ONLY super() and local property init — nothing else is safe
- ๐กconnectedCallback: subscribes to channels — disconnectedCallback MUST unsubscribe
- ๐กrenderedCallback with @track modification: infinite loop without boolean guard
๐ค One-Line Answer for Interview
"Complete LWC lifecycle: constructor (super() + local init) → wire (data fetch) → connectedCallback (DOM exists, subscriptions) → render (shadow DOM created) → renderedCallback (DOM queryable, external library init) → disconnectedCallback (cleanup). Wire fires before connectedCallback."
Q
Question 101 · ๐ด Advanced
How do you build a file preview component in LWC?
✅ Answer
Use lightning-file-upload + ContentVersion wire to list files + NavigationMixin to preview. For inline preview use ContentDocument with URL! ๐
import { getRelatedListRecords } from 'lightning/uiRelatedListApi';
import { NavigationMixin } from 'lightning/navigation';
export default class FilePreview
extends NavigationMixin(LightningElement) {
@api recordId;
@wire(getRelatedListRecords, {
parentRecordId: '$recordId',
relatedListId: 'AttachedContentDocuments',
fields: [
'ContentDocument.Title',
'ContentDocument.FileType',
'ContentDocument.ContentSize',
'ContentDocument.CreatedDate'
],
sortBy: ['ContentDocument.CreatedDate']
})
wiredFiles;
get files() {
return this.wiredFiles?.data?.records?.map(r => ({
id: r.fields.ContentDocument.value.fields.Id.value,
title: r.fields.ContentDocument.value.fields.Title.value,
type: r.fields.ContentDocument.value.fields.FileType.value,
url: `/sfc/servlet.shepherd/document/download/${r.fields.ContentDocument.value.fields.Id.value}`
})) || [];
}
handlePreview(event) {
const docId = event.currentTarget.dataset.id;
this[NavigationMixin.Navigate]({
type: 'standard__namedPage',
attributes: { pageName: 'filePreview' },
state: { selectedRecordId: docId }
});
}
get hasFiles() { return this.files.length > 0; }
}
๐ Key Points for Interviewer
- ๐ฅUse standard__namedPage with pageName:filePreview for native Salesforce file preview modal
- ๐กContentDocument URL: /sfc/servlet.shepherd/document/download/{Id} for direct download
- ๐กAttachedContentDocuments related list: shows files attached to any record
- ๐กFileType field: PDF, PNG, JPG, DOCX — use for icon selection in UI
๐ค One-Line Answer for Interview
"File preview uses getRelatedListRecords on AttachedContentDocuments, extracts ContentDocument fields, and NavigationMixin.Navigate with standard__namedPage filePreview for the native preview modal. Direct download URL: /sfc/servlet.shepherd/document/download/{Id}."
Q
Question 102 · ๐ Intermediate
How do you implement debouncing and throttling in LWC?
✅ Answer
Debounce: delay execution until typing stops (search input). Throttle: limit execution to once per interval (scroll/resize). Both use clearTimeout/setTimeout! ⏱️
export default class DebounceThrottle extends LightningElement {
// DEBOUNCE — waits until user stops typing
debounceTimer;
handleSearchInput(event) {
clearTimeout(this.debounceTimer);
const value = event.target.value;
this.debounceTimer = setTimeout(() => {
this.performSearch(value); // fires 300ms after last keystroke
}, 300);
}
// THROTTLE — fires at most once per interval
throttleTimer = null;
handleScroll(event) {
if (this.throttleTimer) return; // already waiting
this.throttleTimer = setTimeout(() => {
this.handleScrollAction(event); // fires once per 200ms
this.throttleTimer = null; // reset for next interval
}, 200);
}
// Cleanup
disconnectedCallback() {
clearTimeout(this.debounceTimer);
clearTimeout(this.throttleTimer);
}
}
| Pattern | When to Use | Typical Delay |
|---|---|---|
| Debounce | Search inputs, form validation on change | 300-500ms |
| Throttle | Scroll events, resize events, mouse move | 100-200ms |
| Neither needed | Button clicks, form submit, navigation | Use once-only guards |
๐ Key Points for Interviewer
- ๐ฅAlways clearTimeout in disconnectedCallback — pending timer can fire after removal
- ๐กDebounce: clearTimeout + setTimeout on EVERY keystroke — previous timer cancelled
- ๐กThrottle: if timer exists, skip. When timer fires, reset to null for next cycle
- ๐กLodash debounce/throttle: available if Lodash is loaded as static resource
๐ค One-Line Answer for Interview
"Debounce (clearTimeout + setTimeout on every input) waits until user stops typing — ideal for search. Throttle (skip if timer exists) limits execution rate — ideal for scroll/resize. Always clear timers in disconnectedCallback to prevent post-removal execution."
Q
Question 103 · ๐ด Advanced
How do you implement a toast notification system in Experience Cloud?
✅ Answer
Experience Cloud has no ShowToastEvent — build a custom notification LWC with auto-dismiss timer, multiple toast queue, and SLDS styling! ๐
// customToast.js
export default class CustomToast extends LightningElement {
@track toasts = [];
toastIdCounter = 0;
// Called by parent via @api method
@api showToast(title, message, variant = 'info', duration = 3000) {
const id = ++this.toastIdCounter;
this.toasts = [...this.toasts, { id, title, message, variant }];
// Auto-dismiss
setTimeout(() => {
this.removeToast(id);
}, duration);
}
removeToast(id) {
this.toasts = this.toasts.filter(t => t.id !== id);
}
handleClose(event) {
this.removeToast(parseInt(event.currentTarget.dataset.id));
}
}
// HTML
// <template for:each={toasts} for:item="toast">
// <div key={toast.id}
// class={toastClass}
// data-id={toast.id}>
// <strong>{toast.title}</strong>
// <p>{toast.message}</p>
// <button onclick={handleClose} data-id={toast.id}>✕</button>
// </div>
// </template>
// CSS (fixed positioning)
// .toast-container { position:fixed; top:20px; right:20px; z-index:9999; }
// .toast { background:#fff; border-radius:8px; padding:16px;
// box-shadow:0 4px 20px rgba(0,0,0,0.15); margin-bottom:8px; }
// .success { border-left:4px solid #10B981; }
// .error { border-left:4px solid #EF4444; }
๐ Key Points for Interviewer
- ๐ฅFixed position with high z-index — ensures toasts appear above all content
- ๐ก@api showToast() method: parent calls this.template.querySelector('c-custom-toast').showToast(...)
- ๐กAuto-dismiss: store timeout ID per toast to cancel if manually dismissed early
- ๐กMultiple toasts: array of toast objects with unique IDs — supports stacking
๐ค One-Line Answer for Interview
"Experience Cloud custom toast: fixed-position container with @api showToast() method, auto-dismiss setTimeout, and manual close. Render toast queue as for:each array with unique IDs. Parent accesses the component via querySelector and calls the @api method."
Q
Question 104 · ๐ Intermediate
How do you handle keyboard navigation in custom LWC components?
✅ Answer
Implement onkeydown with key-specific handlers (ArrowUp/Down for lists, Enter/Space for selection, Escape for close, Tab for focus management)! ⌨️
// Custom dropdown with full keyboard support
export default class KeyboardNav extends LightningElement {
@track isOpen = false;
@track focusedIndex = -1;
@api options = [];
handleKeyDown(event) {
switch(event.key) {
case 'Enter':
case ' ': // Space
event.preventDefault();
this.isOpen ? this.selectFocused() : this.openDropdown();
break;
case 'Escape':
this.closeDropdown();
break;
case 'ArrowDown':
event.preventDefault(); // stop page scroll
this.focusedIndex = Math.min(
this.focusedIndex + 1,
this.options.length - 1
);
this.isOpen = true;
break;
case 'ArrowUp':
event.preventDefault();
this.focusedIndex = Math.max(this.focusedIndex - 1, 0);
break;
case 'Home':
this.focusedIndex = 0;
break;
case 'End':
this.focusedIndex = this.options.length - 1;
break;
case 'Tab':
this.closeDropdown(); // close on tab away
break;
}
}
selectFocused() {
if (this.focusedIndex >= 0) {
const selected = this.options[this.focusedIndex];
this.dispatchEvent(new CustomEvent('select',
{ detail: { value: selected.value } }));
this.closeDropdown();
}
}
}
๐ Key Points for Interviewer
- ๐ฅevent.preventDefault() on Arrow keys — prevents page scroll while navigating list
- ๐กARIA pattern: role="listbox" + role="option" + aria-selected for screen reader support
- ๐กFocus management: after opening dropdown, programmatically focus the first option
- ๐กTab key: always close dropdown — user expects Tab to navigate away from the component
๐ค One-Line Answer for Interview
"Keyboard navigation requires onkeydown with handlers for Enter/Space (select), Escape (close), ArrowUp/Down (navigate), Tab (close and move on). Always event.preventDefault() on arrow keys to prevent page scrolling. Add ARIA roles for screen reader support."
Q
Question 105 · ๐ด Advanced
How do you implement optimistic UI updates in LWC?
✅ Answer
Update local state immediately for instant feedback, call Apex in background, revert on failure. Users perceive instant response even with network latency! ⚡
export default class OptimisticUi extends LightningElement {
@track records = [];
@track deletingIds = new Set();
// Optimistic delete
async handleDelete(event) {
const recordId = event.currentTarget.dataset.id;
// STEP 1: Update UI immediately (optimistic)
const deletedRecord = this.records.find(r => r.Id === recordId);
this.records = this.records.filter(r => r.Id !== recordId);
try {
// STEP 2: Call server in background
await deleteRecord({ recordId });
this.showToast('Deleted', 'Record deleted', 'success');
} catch (error) {
// STEP 3: Revert on failure
this.records = [...this.records, deletedRecord].sort(
(a, b) => a.Name.localeCompare(b.Name)
);
this.showToast('Error', error.body.message, 'error');
}
}
// Optimistic status toggle
async handleToggleStatus(event) {
const id = event.currentTarget.dataset.id;
// Immediately flip the local status
this.records = this.records.map(r =>
r.Id === id ? { ...r, IsActive__c: !r.IsActive__c } : r
);
try {
await toggleRecordStatus({ recordId: id });
} catch (error) {
// Revert — flip back
this.records = this.records.map(r =>
r.Id === id ? { ...r, IsActive__c: !r.IsActive__c } : r
);
this.showToast('Error', error.body.message, 'error');
}
}
}
๐ Key Points for Interviewer
- ๐ฅSave the old state before optimistic update — you need it for reversion on failure
- ๐กOptimistic UI: best for toggle, delete, reorder — where failure is rare
- ๐กAvoid optimistic UI for: payment processing, data creation with generated IDs, complex workflows
- ๐กLoading states: show subtle spinner alongside optimistic update to indicate background processing
๐ค One-Line Answer for Interview
"Optimistic UI: update local @track state immediately for instant feedback, call Apex in background, and revert to saved state on error. Save the previous state before updating so you can restore on failure. Best for toggles, deletes, and low-risk status changes."
Q
Question 106 · ๐ด Advanced
How do you implement LWC with Visualforce Page?
✅ Answer
Embed LWC in Visualforce using lightning:container or by wrapping the VF page in a Lightning experience page. Or use the Salesforce Tabs + Visualforce app! ๐
// Option 1: Use lightning:container in Visualforce
// This runs a VF page inside a Lightning container
// myPage.vfp
<apex:page standardController="Account">
<apex:includeLightning/>
<div id="lwcContainer"></div>
<script>
$Lightning.use("c:MyLightningApp", function() {
$Lightning.createComponent(
"c:myLwcWrapper", // LWC wrapped in Aura
{ recordId: '{!Account.Id}' },
"lwcContainer",
function(cmp) {}
);
});
</script>
</apex:page>
// Option 2: Aura wrapper for VF to LWC bridge
// auraWrapper.cmp
<aura:component>
<aura:attribute name="recordId" type="Id"/>
<c:myLwcComponent recordId="{!v.recordId}"/>
</aura:component>
// Best practice: migrate VF pages to Lightning Experience
// Use <lightning:isInApp> to detect Lightning context
| Approach | Complexity | Best For |
|---|---|---|
| lightning:container in VF | Medium | Embedding Aura+LWC in existing VF pages |
| Aura wrapper + Lightning Out | Medium | Classic-to-Lightning migration bridge |
| Migrate to Lightning page | Low (long term) | Modern approach — remove VF dependency |
| VF in iFrame on Lightning page | Low | Quick embed without migration |
๐ Key Points for Interviewer
- ๐ฅDirect LWC in Visualforce is NOT supported — must go through Aura wrapper
- ๐กLightning Out: embed Aura (and thus LWC-via-Aura) in external VF pages
- ๐กLong-term: migrate Visualforce to Lightning pages with LWC — VF is legacy
- ๐ก$Lightning.use requires an Aura app that extends ltng:outApp
๐ค One-Line Answer for Interview
"LWC cannot run directly in Visualforce — use an Aura wrapper component with Lightning Out ($Lightning.use) to embed LWC functionality in VF pages. Best long-term strategy: migrate VF pages to Lightning record/app pages using native LWC."
Q
Question 107 · ๐ Intermediate
What is the LWC @salesforce/apex import and how does lazy loading work?
✅ Answer
Import Apex methods with @salesforce/apex/ClassName.methodName — lazy loaded only when called. Import is a Promise-returning function ready to use immediately! ⚡
// Import syntax
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
import saveAccount from '@salesforce/apex/AccountController.saveAccount';
import { deleteRecord } from 'lightning/uiRecordApi'; // LDS — not Apex
// Wire usage (cacheable=true required)
@wire(getAccounts, { searchKey: '$searchKey' })
wiredAccounts;
// Imperative usage (returns Promise)
async handleSave() {
try {
const result = await saveAccount({ account: this.formData });
console.log('Saved:', result);
} catch (error) {
this.error = error.body.message;
}
}
// Multiple imports from same class
import getLineItems
from '@salesforce/apex/QuoteController.getLineItems';
import saveLineItems
from '@salesforce/apex/QuoteController.saveLineItems';
import deleteLineItem
from '@salesforce/apex/QuoteController.deleteLineItem';
// One import = one method (unlike Aura where you pass method names as strings)
// Salesforce validates the method exists at compile/deploy time
๐ Key Points for Interviewer
- ๐ฅOne import per method — unlike Aura which used string method names (no compile-time validation)
- ๐ก@salesforce/apex imports are validated at deploy time — method must exist and be @AuraEnabled
- ๐กImport is always a function — call it with parameters object, it returns a Promise
- ๐กNamed parameters: always pass as object { paramName: value } — matches Apex @AuraEnabled method params
๐ค One-Line Answer for Interview
"@salesforce/apex/ClassName.methodName imports one Apex method per import — validated at deploy time. Wire usage requires cacheable=true. Imperative calls return Promises. Parameters must match @AuraEnabled Apex method parameter names exactly."
Q
Question 108 · ๐ด Advanced
How do you build a notification badge / counter component in LWC?
✅ Answer
Notification badge: @api count, computed CSS class for pulse animation when count > 0, Platform Event or scheduled poll to update count! ๐
// notificationBadge.js
export default class NotificationBadge extends LightningElement {
@api count = 0;
@api label = 'Notifications';
pollingInterval;
connectedCallback() {
this.loadCount();
// Poll every 30 seconds
this.pollingInterval = setInterval(() => {
this.loadCount();
}, 30000);
}
disconnectedCallback() {
if (this.pollingInterval) {
clearInterval(this.pollingInterval); // ← MUST clear interval
}
}
async loadCount() {
try {
this.count = await getUnreadCount();
} catch(e) { /* silent fail for badge */ }
}
get badgeClass() {
let base = 'badge';
if (this.count > 0) base += ' has-notifications pulse';
return base;
}
get displayCount() {
return this.count > 99 ? '99+' : this.count;
}
get hasCount() { return this.count > 0; }
}
// CSS — pulse animation
// @keyframes pulse { 0%{transform:scale(1)} 50%{transform:scale(1.2)} 100%{transform:scale(1)} }
// .pulse { animation: pulse 1.5s infinite; }
๐ Key Points for Interviewer
- ๐ฅclearInterval in disconnectedCallback — setInterval persists forever without cleanup
- ๐กPrefer Platform Events over polling for real-time — polling adds server load
- ๐ก99+ display: cap at 99+ for badge overflow prevention
- ๐กpulse animation: CSS @keyframes on has-notifications class — draws attention without code
๐ค One-Line Answer for Interview
"Notification badge uses setInterval for polling (cleared in disconnectedCallback) or Platform Events for real-time. Count getter caps at 99+. Pulse animation via CSS @keyframes on has-notifications class."
Q
Question 109 · ๐ Intermediate
How do you pass complex objects between parent and child in LWC?
✅ Answer
Pass objects via @api property (parent → child) and CustomEvent detail (child → parent). Important: never mutate @api object in child — spread to make a copy! ๐ฆ
// Parent → Child: pass object via @api
// parentComp.html
<c-child-form
config={formConfig}
onsave={handleSave}>
</c-child-form>
// parentComp.js
formConfig = {
title: 'Edit Account',
fields: ['Name', 'Phone', 'Type'],
isRequired: true
};
// Child receives via @api
export default class ChildForm extends LightningElement {
@api config; // receives the full object
// ❌ WRONG — mutating @api object
handleChange() {
this.config.title = 'New Title'; // Error in strict mode!
}
// ✅ CORRECT — work with local copy
localConfig;
connectedCallback() {
this.localConfig = { ...this.config }; // shallow copy
}
// Child → Parent: return modified object via CustomEvent
handleSave() {
this.dispatchEvent(new CustomEvent('save', {
detail: { config: { ...this.localConfig } }
}));
}
}
// Parent handles:
handleSave(event) {
this.formConfig = event.detail.config;
}
๐ Key Points for Interviewer
- ๐ฅChild CANNOT mutate @api objects — LWC throws error in strict mode
- ๐กAlways spread @api object to local copy in connectedCallback if child needs to modify it
- ๐กDeep objects: JSON.parse(JSON.stringify(obj)) for deep clone — spread is shallow
- ๐กParent always owns the data — child gets a read-only view via @api
๐ค One-Line Answer for Interview
"Pass complex objects via @api (parent to child) and CustomEvent detail (child to parent). Never mutate @api objects in child — spread to create a local working copy in connectedCallback. Parent always owns and controls the source data."
Q
Question 110 · ๐ด Advanced
How do you implement real-time collaboration features in LWC?
✅ Answer
Real-time collaboration uses Platform Events (EmpApi) for pub/sub, Change Data Capture for record sync, and presence indicators via polling or streaming! ๐ฅ
// Real-time presence indicator
export default class RealtimeCollaboration extends LightningElement {
@api recordId;
activeUsers = [];
subscription;
heartbeatInterval;
connectedCallback() {
this.registerPresence();
this.subscribeToPresence();
// Heartbeat every 30 seconds to maintain presence
this.heartbeatInterval = setInterval(
() => this.registerPresence(), 30000
);
}
async registerPresence() {
// Publish presence event via Apex
await publishPresenceEvent({
recordId: this.recordId,
userId: USER_ID,
action: 'VIEWING'
});
}
subscribeToPresence() {
subscribe('/event/UserPresence__e', -1, (msg) => {
if (msg.data.payload.RecordId__c === this.recordId) {
this.updateActiveUsers(msg.data.payload);
}
}).then(sub => this.subscription = sub);
}
disconnectedCallback() {
clearInterval(this.heartbeatInterval);
if (this.subscription) unsubscribe(this.subscription, () => {});
// Publish departure event
publishPresenceEvent({
recordId: this.recordId,
userId: USER_ID,
action: 'LEFT'
});
}
}
๐ Key Points for Interviewer
- ๐ฅAlways publish a departure event in disconnectedCallback — clean presence list
- ๐กHeartbeat: re-publish presence event every 30s to indicate still active
- ๐กPlatform Events for presence: fire UserPresence__e on arrival, departure, and heartbeat
- ๐กCDC alternative: subscribe to /data/AccountChangeEvent for automatic record change notifications
๐ค One-Line Answer for Interview
"Real-time collaboration uses Platform Events (EmpApi) for presence pub/sub — publish on arrival/departure/heartbeat, subscribe to update active user list. CDC provides automatic record change notifications. Always unsubscribe and publish departure event in disconnectedCallback."
Q
Question 111 · ๐ Intermediate
How do you create reusable form components in LWC?
✅ Answer
Reusable form: @api initialValues for pre-filling, @api schema for field config, validate() @api method, fire submit CustomEvent with form data! ๐
// reusableForm.js
export default class ReusableForm extends LightningElement {
@api schema = []; // [{ name, label, type, required, options }]
@api initialValues = {};
@track formData = {};
connectedCallback() {
// Pre-fill from initialValues
this.formData = { ...this.initialValues };
}
handleFieldChange(event) {
const field = event.target.dataset.field;
const value = event.target.type === 'checkbox'
? event.target.checked
: event.target.value;
this.formData = { ...this.formData, [field]: value };
}
// @api method — parent can call to trigger validation
@api validate() {
const inputs = this.template.querySelectorAll('lightning-input, lightning-combobox');
return [...inputs].every(input => input.reportValidity());
}
// @api method — parent can call to reset
@api reset() {
this.formData = { ...this.initialValues };
this.template.querySelectorAll('lightning-input').forEach(
i => i.value = ''
);
}
handleSubmit() {
if (!this.validate()) return;
this.dispatchEvent(new CustomEvent('formsubmit', {
detail: { data: { ...this.formData } }
}));
}
}
๐ Key Points for Interviewer
- ๐ฅExpose validate() and reset() as @api methods — parent can trigger without re-rendering
- ๐กschema @api: array of field definitions allows the same component for different forms
- ๐กformData spread in handleFieldChange: immutable update triggers reactivity
- ๐กFire formsubmit event with data copy — parent should not receive a reference to internal state
๐ค One-Line Answer for Interview
"Reusable form exposes @api schema for field definitions, @api initialValues for pre-filling, @api validate() for parent-triggered validation, and fires formsubmit CustomEvent with data. Spread formData on update for immutable reactive patterns."
Q
Question 112 · ๐ด Advanced
How do you implement a configurable dashboard in LWC?
✅ Answer
Configurable dashboard: @api widgetConfig array drives dynamic component rendering via lwc:component, user can add/remove/reorder widgets, config saved to Custom Metadata or Custom Setting! ๐
// dashboard.js
export default class ConfigurableDashboard extends LightningElement {
@track widgets = [];
@track isEditMode = false;
connectedCallback() {
this.loadDashboardConfig();
}
async loadDashboardConfig() {
const config = await getDashboardConfig({ userId: USER_ID });
this.widgets = await Promise.all(
config.map(async (w) => ({
...w,
constructor: await load(`c/${w.componentName}`)
}))
);
}
handleToggleEdit() {
this.isEditMode = !this.isEditMode;
}
handleRemoveWidget(event) {
const id = event.detail.widgetId;
this.widgets = this.widgets.filter(w => w.id !== id);
this.saveConfig();
}
async saveConfig() {
const config = this.widgets.map(w => ({
id: w.id, componentName: w.componentName,
position: w.position
}));
await saveDashboardConfig({ userId: USER_ID, config: JSON.stringify(config) });
}
}
๐ Key Points for Interviewer
- ๐ฅDynamic widgets use lwc:component + lwc:is with dynamically loaded constructors
- ๐กPromise.all: load all widget constructors in parallel — faster than sequential
- ๐กConfig persistence: Custom Metadata for org-wide defaults, Custom Setting for per-user config
- ๐กEdit mode: expose remove/reorder controls only in edit mode — prevents accidental changes
๐ค One-Line Answer for Interview
"Configurable dashboard uses lwc:component with dynamically loaded constructors (lwc:is), stores widget configuration per-user in Custom Settings, and uses Promise.all to load all widget constructors in parallel. Edit mode controls expose add/remove/reorder actions."
Q
Question 113 · ๐ Intermediate
How do you handle concurrent API calls and race conditions in LWC?
✅ Answer
Race conditions occur when fast typing triggers multiple Apex calls and an older response arrives after a newer one. Fix with request ID tracking or AbortController! ๐️
export default class SearchWithRaceCondition extends LightningElement {
@track results = [];
requestId = 0; // Tracks the latest request
debounceTimer;
handleSearch(event) {
clearTimeout(this.debounceTimer);
const term = event.target.value;
this.debounceTimer = setTimeout(
() => this.performSearch(term), 300
);
}
async performSearch(term) {
const currentRequestId = ++this.requestId; // Increment for each request
try {
const data = await searchAccounts({ searchTerm: term });
// Only update if this is still the latest request
if (currentRequestId === this.requestId) {
this.results = data;
}
// If not latest: silently discard stale response ✅
} catch (error) {
if (currentRequestId === this.requestId) {
this.error = error.body.message;
}
}
}
}
// Alternative: AbortController (for fetch-based calls)
controller = null;
async performFetch(term) {
if (this.controller) this.controller.abort(); // cancel previous
this.controller = new AbortController();
// Pass signal to fetch: fetch(url, { signal: this.controller.signal })
}
๐ Key Points for Interviewer
- ๐ฅRequest ID pattern: increment counter per request, only update UI if counter matches latest
- ๐กDebounce reduces race conditions significantly — but doesn't eliminate them
- ๐กAbortController: best for native fetch calls — cancels in-flight HTTP request
- ๐กApex calls: cannot abort mid-flight, use request ID pattern to discard stale responses
๐ค One-Line Answer for Interview
"Race conditions in search: increment a requestId counter per call, only update UI if the response's requestId matches the current counter. Debounce reduces frequency. AbortController for fetch calls; requestId pattern for Apex calls which cannot be cancelled."
Q
Question 114 · ๐ด Advanced
How do you implement a virtual scroll / windowed list in LWC?
✅ Answer
Virtual scroll renders only visible rows — tracks scroll position, calculates visible range, and renders a subset with spacers for non-rendered rows. Handles millions of rows! ๐ฒ
export default class VirtualScroll extends LightningElement {
allRecords = []; // all data (loaded from server)
@track visibleRecords = []; // only what renders
itemHeight = 50; // fixed row height in px
visibleCount = 20; // rows to render at once
startIndex = 0;
containerHeight = 0;
totalHeight = 0;
get topSpacerHeight() {
return this.startIndex * this.itemHeight + 'px';
}
get bottomSpacerHeight() {
const remaining = Math.max(
0, this.allRecords.length - this.startIndex - this.visibleCount
);
return remaining * this.itemHeight + 'px';
}
handleScroll(event) {
const scrollTop = event.target.scrollTop;
const newStartIndex = Math.floor(scrollTop / this.itemHeight);
if (newStartIndex !== this.startIndex) {
this.startIndex = newStartIndex;
this.visibleRecords = this.allRecords.slice(
this.startIndex,
this.startIndex + this.visibleCount
);
}
}
// HTML structure:
// <div class="scroll-container" onscroll={handleScroll}>
// <div style={topSpacerHeight}></div> // pushes items down
// <template for:each={visibleRecords} for:item="rec">
// <div key={rec.Id} class="row">{rec.Name}</div>
// </template>
// <div style={bottomSpacerHeight}></div> // maintains scroll height
// </div>
}
๐ Key Points for Interviewer
- ๐ฅVirtual scroll renders only ~20 rows at any time regardless of total record count
- ๐กTop and bottom spacer divs maintain correct scroll bar position and height
- ๐กFixed row height: required for accurate scroll position calculation
- ๐กFor variable row heights: use IntersectionObserver with position caching
๐ค One-Line Answer for Interview
"Virtual scroll renders only visible rows using top/bottom spacer divs to maintain scroll position. Calculate startIndex from scrollTop/itemHeight, slice allRecords for visibleRecords. Renders ~20 rows regardless of total data size."
Q
Question 115 · ๐ด Advanced
What is LWC Reactive Properties deep dive — when does re-render trigger?
✅ Answer
Primitive @track properties re-render on value change. Objects re-render only on reference change (shallow). Deep mutations require @track or spread. Know all trigger rules! ๐
// Primitive — re-renders on any change
count = 0;
handleIncrement() {
this.count++; // ✅ triggers re-render (new value)
}
// Object WITHOUT @track
filters = { type: '', status: '' };
handleChange() {
this.filters.type = 'Active'; // ❌ NO re-render
this.filters = { ...this.filters, type: 'Active' }; // ✅ new ref = re-render
}
// Array WITHOUT @track
items = [];
handlePush() {
this.items.push(newItem); // ❌ NO re-render
this.items = [...this.items, newItem]; // ✅ new ref = re-render
}
// @track — re-renders on DEEP property changes
@track config = { title: '', nested: { value: 0 } };
handleDeepChange() {
this.config.nested.value = 42; // ✅ @track watches deep changes
}
// @wire reactive param — re-fires wire on $ param change
@api recordId; // primitive
@wire(getRecord, { recordId: '$recordId' }) // re-fires when recordId changes
wiredRecord;
| Property Type | Mutation Type | Triggers Re-render? |
|---|---|---|
| Primitive (string/number) | Direct assignment | ✅ Yes |
| Object (no @track) | Property mutation | ❌ No |
| Object (no @track) | New reference (spread) | ✅ Yes |
| @track Object | Deep property mutation | ✅ Yes |
| Array (no @track) | push/splice/sort | ❌ No |
| Array (no @track) | New reference (spread) | ✅ Yes |
๐ Key Points for Interviewer
- ๐ฅWithout @track: always create new reference (spread/map/filter) to trigger re-render
- ๐ก@track deep watching is expensive — use only for genuinely nested reactive structures
- ๐ก@api properties: reactive when parent changes the value — child cannot mutate them
- ๐กWire $ param: any change to the reactive property immediately triggers re-fetch
๐ค One-Line Answer for Interview
"LWC re-renders on: any primitive value change, new object/array reference (spread). No re-render on: object property mutation without spread. @track enables deep reactivity — watches nested property changes. Always use spread ([...array] or {...obj}) for objects without @track."
Q
Question 116 · ๐ Intermediate
What is the LWC Service Component pattern? When do you use it?
✅ Answer
Service component = LWC bundle with ONLY a JavaScript file exporting utility functions — no HTML, no default class. The LWC equivalent of an Apex utility class! ๐ง
// lwcUtils/lwcUtils.js
// NO HTML FILE, NO default class export
export function formatCurrency(amount, currency = 'INR') {
return new Intl.NumberFormat('en-IN', {
style: 'currency', currency
}).format(amount);
}
export function formatDate(dateStr, locale = 'en-IN') {
return new Date(dateStr).toLocaleDateString(locale, {
day: '2-digit', month: 'short', year: 'numeric'
});
}
export function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
export function extractError(error) {
if (Array.isArray(error.body))
return error.body.map(e => e.message).join(', ');
return error?.body?.message || error?.message || 'Unknown error';
}
export function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// Any component imports and uses:
import { formatCurrency, formatDate, extractError }
from 'c/lwcUtils';
get formattedAmount() {
return formatCurrency(this.amount);
}
๐ Key Points for Interviewer
- ๐ฅService component: NO html file, NO default class — just named function exports
- ๐กImport with named imports: import { fn1, fn2 } from 'c/serviceName'
- ๐กShare error extraction, formatting, validation, and debounce utilities across all components
- ๐กOne service component per concern: lwcUtils (general), formUtils (form validation), dateUtils (dates)
๐ค One-Line Answer for Interview
"Service component is an LWC bundle with only a JS file exporting named utility functions — no HTML, no default class. Import named functions from 'c/serviceName'. This is the LWC equivalent of an Apex utility class for sharing formatters, validators, and error extractors."
Q
Question 117 · ๐ด Advanced
How do you implement LWC unit tests with complex scenarios?
✅ Answer
Advanced Jest patterns: mock imperative Apex, simulate form submit, test error boundaries, mock NavigationMixin, and test event dispatching! ๐งช
import { createElement } from 'lwc';
import MyComp from 'c/myComp';
import { registerApexTestWireAdapter, registerLdsTestWireAdapter }
from '@salesforce/sfdx-lwc-jest';
import saveAccount from '@salesforce/apex/AccountController.saveAccount';
import { getRecord } from 'lightning/uiRecordApi';
// Mock NavigationMixin
const mockNavigate = jest.fn();
jest.mock('lightning/navigation', () => ({
NavigationMixin: (Base) => class extends Base {
[Symbol.for('LWC public method navigate')] = mockNavigate;
}
}));
// Mock imperative Apex
jest.mock('@salesforce/apex/AccountController.saveAccount',
() => jest.fn(), { virtual: true });
describe('MyComp tests', () => {
const mockGetRecord = registerLdsTestWireAdapter(getRecord);
it('handles Apex error gracefully', async () => {
const element = createElement('c-my-comp', { is: MyComp });
document.body.appendChild(element);
// Mock Apex to reject
saveAccount.mockRejectedValue(
{ body: { message: 'Save failed' } }
);
// Trigger save
element.shadowRoot.querySelector('.save-btn').click();
await Promise.resolve();
await Promise.resolve(); // wait for async
// Assert error shown
const error = element.shadowRoot.querySelector('.error-msg');
expect(error.textContent).toContain('Save failed');
document.body.removeChild(element);
});
});
๐ Key Points for Interviewer
- ๐ฅMock NavigationMixin with jest.mock to test navigation without actual page change
- ๐กsaveAccount.mockResolvedValue(result) for success path; .mockRejectedValue() for error path
- ๐กMultiple await Promise.resolve() for chained async operations in tests
- ๐กregisterLdsTestWireAdapter for LDS adapters (getRecord); registerApexTestWireAdapter for custom Apex wires
๐ค One-Line Answer for Interview
"Advanced Jest: mock NavigationMixin with jest.mock, mock imperative Apex with jest.fn().mockResolvedValue/mockRejectedValue, use multiple await Promise.resolve() for chained async, and registerLdsTestWireAdapter for LDS wire adapters."
Q
Question 118 · ๐ Intermediate
How do you implement LWC component communication via shared Apex state?
✅ Answer
When two unrelated components need the same data, share state via custom LDS wire adapter pattern or a singleton Apex service — one fetches, one subscribes! ๐
// Pattern: Platform Event as state sync
// Component A updates → publishes Platform Event → Component B refreshes
// Component A (writer)
async handleSave() {
await updateRecord({ fields });
// Notify via Platform Event that data changed
await publishRefreshEvent({ objectName: 'Opportunity' });
}
// Component B (reader — subscribes to refresh events)
subscribeToRefreshEvents() {
subscribe('/event/DataRefresh__e', -1, (msg) => {
if (msg.data.payload.ObjectName__c === 'Opportunity') {
refreshApex(this.wiredOpportunities);
}
}).then(sub => this.subscription = sub);
}
// Alternative: LMS as state sync
// Component A publishes LMS message after DML
publish(this.messageContext, REFRESH_CHANNEL, {
action: 'REFRESH',
objectName: 'Opportunity'
});
// Component B subscribes to LMS
connectedCallback() {
this.sub = subscribe(this.messageContext, REFRESH_CHANNEL,
(msg) => {
if (msg.action === 'REFRESH') {
refreshApex(this.wiredData);
}
},
{ scope: APPLICATION_SCOPE }
);
}
๐ Key Points for Interviewer
- ๐ฅPlatform Events: real-time sync even across browser tabs (via server)
- ๐กLMS: sync within same page only — faster for same-page components
- ๐กrefreshApex() in subscriber keeps wire cache in sync after Platform Event or LMS
- ๐กAnti-pattern: polling for state changes — use event-driven patterns instead
๐ค One-Line Answer for Interview
"Share Apex state between unrelated components using Platform Events (cross-tab real-time sync) or LMS (same-page sync). After DML: publisher fires event, subscriber calls refreshApex() to update wire cache. Avoid polling — use event-driven state synchronization."
Q
Question 119 · ๐ด Advanced
What are the LWC Anti-Patterns to avoid in enterprise Salesforce development?
✅ Answer
Top LWC anti-patterns: deep @api prop drilling, god components, direct DOM manipulation, mixing concerns, no cleanup, loading all records, and no error handling! ❌
| Anti-Pattern | Problem | Better Approach |
|---|---|---|
| @api prop drilling 4+ levels | Fragile, hard to change | Use LMS or service module |
| God component (500+ lines) | Unreadable, untestable | Split into child components |
| All records in memory | Browser crash, slow render | Server-side pagination |
| No disconnectedCallback cleanup | Memory leaks, phantom subscriptions | Always unsubscribe, destroy, clearInterval |
| Apex call on every keystroke | Governor limits, performance | Debounce + minimum characters |
| Mutating @api objects | Runtime errors, unpredictable behavior | Spread to local copy first |
| No error handling | Broken UI with no feedback | Always handle wire error and Apex .catch() |
| renderedCallback without guard | Infinite re-render loop | Boolean guard: if(this.initialized) return |
// ❌ God component anti-pattern
// myHugeComponent.js — 800 lines, handles:
// - Data fetching
// - Form validation
// - Chart rendering
// - Export logic
// - Modal management
// ✅ Decomposed — each component does ONE thing
// dataTable.js — renders table
// filterPanel.js — manages filters
// chartPanel.js — renders chart
// exportButton.js — handles export
// parentContainer.js — coordinates via @api + events
// ❌ No error boundary
@wire(getData, { id: '$recordId' })
wiredData; // only uses data, ignores error!
// ✅ Always handle both
@wire(getData, { id: '$recordId' })
wiredData({ data, error }) {
if (data) this.data = data;
if (error) this.error = this.extractError(error);
}
๐ Key Points for Interviewer
- ๐ฅGod components are the #1 LWC architecture problem in enterprise orgs
- ๐กSingle Responsibility: one component = one job — easier to test, reuse, and maintain
- ๐กMissing cleanup is the #2 problem — EmpApi subscriptions persist forever without disconnect
- ๐กPerformance anti-patterns compound: god component + all records + no debounce = production incident
๐ค One-Line Answer for Interview
"Top LWC anti-patterns: god components (split by responsibility), deep @api prop drilling (use LMS), no disconnectedCallback cleanup (memory leaks), loading all records (paginate), missing error handling (always handle wire errors), and renderedCallback without boolean guard (infinite loops)."
Q
Question 120 · ๐ด Advanced
What are the essential topics to master for LWC certification and senior interviews?
✅ Answer
Master the 10 core pillars: lifecycle, reactivity, communication patterns, LDS, wire adapters, performance, Shadow DOM, testing, accessibility, and real component builds. ๐ฏ
// The 10 LWC Mastery Pillars:
// 1. LIFECYCLE — All 5 hooks + wire order + cleanup
// constructor → wire → connectedCallback → render → renderedCallback
// Always clean up: disconnectedCallback
// 2. REACTIVITY — When re-renders trigger
// Primitive: direct change | Object: new reference | @track: deep change
// 3. COMMUNICATION — All 4 patterns
// @api, CustomEvent, LMS, Service Module
// 4. LDS — All wire adapters, uiRecordApi CRUD
// getRecord, getObjectInfo, getPicklistValues, uiRecordApi CRUD
// 5. APEX — Wire vs imperative, cacheable, refreshApex
// Wire: cacheable=true, reactive | Imperative: DML, Promise
// 6. PERFORMANCE — Memoize, pagination, debounce, lazy load
// Memoize getters, server pagination, Promise.all, lazy render
// 7. SHADOW DOM — CSS custom properties, querySelector, slots
// Slots, :host, CSS vars pierce shadow, lwc:ref
// 8. TESTING — Jest patterns
// createElement, wire mock, shadowRoot.querySelector, cleanup
// 9. ACCESSIBILITY — ARIA, keyboard, WCAG
// aria-label, role, tabindex, keyboard handlers
// 10. REAL BUILDS — Practical components
// Lookup, datatable inline edit, modal, wizard, kanban
| Topic | Weight in Senior Interviews |
|---|---|
| Lifecycle hooks order | High — always asked |
| Wire vs imperative | High — always asked |
| All communication patterns | High — with scenario judgment |
| Performance patterns | Medium-High — at scale |
| Shadow DOM / CSS | Medium — practical knowledge |
| Jest testing | Medium — for development roles |
| Real component builds | High — hands-on tests |
๐ Key Points for Interviewer
- ๐ฅSenior LWC interviews test WHY and WHEN — not just HOW
- ๐กPrepare real component builds: custom lookup, inline edit datatable, modal, stepper
- ๐กKnow all 4 communication patterns cold — and explain which to use for each scenario
- ๐กCertification exam: focus on wire adapters, lifecycle, @api/@track/@wire decorators, LDS
๐ค One-Line Answer for Interview
"Master 10 LWC pillars for certification and senior interviews: lifecycle order, reactivity rules, all 4 communication patterns, LDS wire adapters, wire vs imperative Apex, performance patterns, Shadow DOM, Jest testing, accessibility, and hands-on component builds."
๐ Also Read:
→ LWC Part 1 (Q1–Q40)
→ LWC Part 2 (Q41–Q80)
→ Apex Triggers Interview Questions
→ Field Service Lightning 125 Q&As
→ LWC Part 1 (Q1–Q40)
→ LWC Part 2 (Q41–Q80)
→ Apex Triggers Interview Questions
→ Field Service Lightning 125 Q&As