๐Ÿ  Home ๐Ÿ”’ Record Sharing ⚙ Apex Triggers ๐Ÿ” SOQL ๐Ÿ’ป LWC ๐Ÿ”— Integration ๐Ÿค– Flows & Automation ๐Ÿค– Agentforce & AI ๐ŸŽˆ Agentforce Course — Free ☁ Data Cloud ๐ŸŽ“ DC Course — Free ๐Ÿš€ DevOps Course — Free ๐Ÿ’ต CPQ ๐ŸŽฏ 100 Scenario Questions ๐Ÿ† 150 Advanced Questions ๐Ÿ“ง Marketing Cloud ๐Ÿ—️ Company Wise ๐Ÿ‘ฅ About Us Start Learning Free →

Salesforce LWC Interview Questions Part 3 2026 — Accessibility, CDC, Kanban, Virtual Scroll & Anti-Patterns

๐Ÿ“…  LWC
Salesforce LWC Interview Questions Part 3 (Q81-Q120) | SF Interview Pro
⚡ 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
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 AttributePurpose
role="button"Tells screen reader the element is interactive
aria-labelAccessible name for the element (overrides text)
aria-live="polite"Screen reader announces dynamic content changes
aria-expandedShows whether collapsible content is open/closed
aria-disabledCommunicates 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 FeatureLocker Behavior
DOM isolationComponent cannot access another component's DOM
Global windowWrapped — some properties blocked or proxied
eval()Blocked — Content Security Policy enforcement
Prototype pollutionPrevented — native prototypes cannot be modified
Cross-namespace accessBlocked — each namespace isolated
LWS vs LockerLWS 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 ValueMeaning
STARTEDFlow has started
PAUSEDUser paused the flow (waits for resume)
FINISHEDScreen flow finished all screens
FINISHED_SCREENLast screen completed
ERRORFlow 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 AdapterImportWhat It Returns
getRecordlightning/uiRecordApiSingle record with fields
getRecordslightning/uiRecordApiMultiple records in one call
getObjectInfolightning/uiObjectInfoApiObject metadata, fields, record types
getPicklistValueslightning/uiObjectInfoApiPicklist field values for record type
getPicklistValuesByRecordTypelightning/uiObjectInfoApiAll picklist values grouped by record type
getRelatedListRecordslightning/uiRelatedListApiRelated list records
getRelatedListInfolightning/uiRelatedListApiRelated list metadata
getNavItemslightning/uiNavigationApiApp navigation items
CurrentPageReferencelightning/navigationCurrent page URL and state
MessageContextlightning/messageServiceLMS context for publish/subscribe
graphqllightning/uiGraphQLApiGraphQL 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 ConceptLWC Equivalent
Component EventsCustomEvent with bubbles/composed
Application EventsLightning Message Service (LMS)
force:createRecordNavigationMixin + standard__objectPage
force:editRecordNavigationMixin + actionName:edit
force:navigateToRecordNavigationMixin + standard__recordPage
{!v.property}Direct property binding {property}
{!c.actionHandler}{actionHandler} — no c. prefix
helper.jsMethods directly in the component class
@api or @track properties
$A.enqueueActionImperative 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. ๐Ÿ›
BugRoot CauseFix
querySelector returns nullCalled in connectedCallback/constructor before DOM rendersMove to renderedCallback or event handler
renderedCallback infinite loopModifying tracked property inside renderedCallback without guardAdd boolean flag: if(this.initialized) return
Wire never firesReactive parameter is undefinedCheck @api receives value; ensure $ prefix on reactive params
Cannot mutate @api propertyChild cannot modify parent-owned dataDispatch CustomEvent with new value; parent updates and passes back
Stale data after DMLLDS cache not refreshedCall refreshApex(this.wiredResult) after imperative DML
CustomEvent not caughtEvent name mismatch or missing bubbles+composedCheck 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)
FeatureMobile 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 AppSeparate 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! ๐Ÿš€
FeatureAPI VersionKey Benefit
lwc:ref + this.refsAPI 59.0+ (Summer '23)Direct element access without selector strings
lwc:component + lwc:isAPI 59.0+Dynamic component rendering at runtime
GraphQL wire adapterAPI 59.0+Multi-object queries in one wire call
Light DOM (renderMode)API 59.0+Remove shadow encapsulation for global CSS
Dynamic Expressions in keysAPI 60.0+More flexible iteration patterns
Agentforce LWC actionsAPI 61.0+LWC as custom actions in Agentforce agents
Enhanced LWC in FlowAPI 61.0+Better LWC screen component for Flows
LWC Signals (preview)UpcomingFine-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 PhaseDOM Available?@api Available?Can Query Shadow?
constructor()❌ No❌ No❌ No
@wire❌ NoPartially❌ 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); } }
PatternWhen to UseTypical Delay
DebounceSearch inputs, form validation on change300-500ms
ThrottleScroll events, resize events, mouse move100-200ms
Neither neededButton clicks, form submit, navigationUse 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
ApproachComplexityBest For
lightning:container in VFMediumEmbedding Aura+LWC in existing VF pages
Aura wrapper + Lightning OutMediumClassic-to-Lightning migration bridge
Migrate to Lightning pageLow (long term)Modern approach — remove VF dependency
VF in iFrame on Lightning pageLowQuick 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 TypeMutation TypeTriggers Re-render?
Primitive (string/number)Direct assignment✅ Yes
Object (no @track)Property mutation❌ No
Object (no @track)New reference (spread)✅ Yes
@track ObjectDeep 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-PatternProblemBetter Approach
@api prop drilling 4+ levelsFragile, hard to changeUse LMS or service module
God component (500+ lines)Unreadable, untestableSplit into child components
All records in memoryBrowser crash, slow renderServer-side pagination
No disconnectedCallback cleanupMemory leaks, phantom subscriptionsAlways unsubscribe, destroy, clearInterval
Apex call on every keystrokeGovernor limits, performanceDebounce + minimum characters
Mutating @api objectsRuntime errors, unpredictable behaviorSpread to local copy first
No error handlingBroken UI with no feedbackAlways handle wire error and Apex .catch()
renderedCallback without guardInfinite re-render loopBoolean 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
TopicWeight in Senior Interviews
Lifecycle hooks orderHigh — always asked
Wire vs imperativeHigh — always asked
All communication patternsHigh — with scenario judgment
Performance patternsMedium-High — at scale
Shadow DOM / CSSMedium — practical knowledge
Jest testingMedium — for development roles
Real component buildsHigh — 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."