12.3 Lightning Web Components Fundamentals
Key Takeaways
- An LWC bundle typically includes .html template, .js module, optional .css, and .js-meta.xml configuration
- @api exposes public properties; modern LWC reactivity tracks field changes without legacy @track on primitives/objects in most cases; @wire declaratively provisions data
- Shadow DOM encapsulates component markup and CSS so styles do not leak in or out by default
- Prefer base Lightning components and SLDS patterns for consistent, accessible UI
- js-meta.xml controls isExposed, targets (App Builder pages, record pages, utilities), and public property definitions for admins
12.3 Lightning Web Components Fundamentals
Quick Answer: A Lightning Web Component is a folder with
componentName.html,componentName.js, optionalcomponentName.css, andcomponentName.js-meta.xml. Use@apifor public properties, modern reactive fields (and@trackonly when still required for legacy nested mutation patterns), and@wirefor declarative data. Shadow DOM encapsulates DOM/CSS. Expose components to App Builder withisExposedandtargetsin the meta file. Prefer base Lightning components and SLDS.
This section is the core LWC vocabulary for Platform Developer I User Interface questions. Section 12.4 extends into Apex wire, imperative calls, LDS, and events.
LWC File Structure
Example bundle accountSummary:
accountSummary/
accountSummary.html
accountSummary.js
accountSummary.css
accountSummary.js-meta.xml
| File | Purpose |
|---|---|
.html | Template markup with directives (lwc:if, for:each, etc. depending on API version patterns you study) and bindings |
.js | ES module exporting a class extending LightningElement |
.css | Styles scoped to the component via Shadow DOM |
.js-meta.xml | Metadata: API version, exposure, targets, properties |
Naming rules matter: the folder name, HTML root tag usage, and JavaScript class naming follow LWC conventions (kebab-case in markup, camelCase class names).
HTML template essentials
<!-- accountSummary.html -->
<template>
<lightning-card title={cardTitle}>
<div class="slds-p-around_medium">
<p>{accountName}</p>
<lightning-button label="Refresh" onclick={handleRefresh}></lightning-button>
</div>
</lightning-card>
</template>
Notes:
- The root is a
<template>wrapper - Bind properties with
{propertyName}(nothis.in the template) - Bind event handlers with
{handlerName} - Use base components like
lightning-cardandlightning-buttonwhen possible
JavaScript module essentials
// accountSummary.js
import { LightningElement, api } from 'lwc';
export default class AccountSummary extends LightningElement {
@api recordId;
@api cardTitle = 'Account Summary';
accountName = 'Loading…';
handleRefresh() {
// imperative logic or refresh wires (see 12.4)
}
}
- One default export class extending
LightningElement - Import decorators and modules from
'lwc'or Lightning platform modules - Class fields hold reactive state for the template
CSS
/* accountSummary.css */
:host {
display: block;
}
p {
font-weight: 600;
}
Styles apply inside the component’s shadow tree. They do not freely style parent page DOM, and outer CSS does not casually restyle your internal elements the way global Visualforce stylesheets often did.
Meta configuration (js-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>62.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Account Summary</masterLabel>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<property name="cardTitle" type="String" label="Card Title" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
| Meta concept | Meaning |
|---|---|
isExposed | true makes the component available for selection in supported builders when targets allow |
targets | Where it can be placed (record page, app page, home page, utility bar, Flow screens, etc.) |
properties | App Builder-configurable public inputs (align with @api fields) |
apiVersion | Component API version |
Exam trap: A component with perfect JS/HTML but isExposed=false (or missing the right target) will not appear in App Builder for that page type.
Decorators and Reactivity
@api — public reactive properties
@api marks a field as a public property that parents (or App Builder) can set:
import { LightningElement, api } from 'lwc';
export default class ChildPanel extends LightningElement {
@api heading;
@api recordId; // often provided on record pages when configured
}
Parent markup:
<c-child-panel heading="Details" record-id={recordId}></c-child-panel>
HTML attribute names are kebab-case; JavaScript properties are camelCase (record-id ↔ recordId).
Public properties are part of the component’s contract. Prefer @api for inputs; keep internal state on non-@api fields.
Modern reactivity and @track
In modern LWC, class fields used in the template are reactive for reassignment. Historically, @track was required to make objects/arrays deeply reactive. Today:
- Reassigning a field (
this.contacts = [...newList]) triggers rerender - Mutating a nested property in place (
this.contacts[0].Name = 'x') may not trigger UI updates unless you use patterns that establish reactivity correctly @trackstill appears in older code and some exam materials for tracking mutations on plain objects/arrays
Exam-ready guidance:
- Prefer immutable updates (spread new objects/arrays) so reactivity is obvious
- Recognize
@trackwhen shown on an object field in legacy snippets - Do not confuse
@track(internal reactivity) with@api(public surface)
import { LightningElement, track } from 'lwc';
export default class FilterPanel extends LightningElement {
// Modern style: reassign fields
filters = { status: 'Open', ownerId: null };
// Legacy-style annotation still seen in materials
@track draft = { name: '' };
setStatus(status) {
this.filters = { ...this.filters, status }; // reactive reassignment
}
}
@wire — declarative data provisioning
@wire connects a property or function to a Lightning data adapter (Apex wire-enabled method, uiRecordApi, and others):
import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Account.Name';
export default class AccountSummary extends LightningElement {
@api recordId;
@wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD] })
account;
get accountName() {
return getFieldValue(this.account.data, NAME_FIELD);
}
}
Wire fundamentals:
- Reactive parameters use
'$recordId'syntax so the wire re-fires whenrecordIdchanges - Wired property receives
{ data, error }shape - Wired function form lets you handle data/error in a method body
- Section 12.4 covers Apex wire, imperative Apex, and error patterns in depth
Other common imports (recognize on sight)
| Import | Role |
|---|---|
LightningElement | Base class |
api, wire, track | Decorators |
lightning/uiRecordApi | LDS-style record access |
@salesforce/schema/... | Compile-time field/object references |
@salesforce/apex/... | Import Apex methods |
Shadow DOM Encapsulation
LWC renders into Shadow DOM, which:
- Scopes CSS to the component
- Hides internal DOM structure from casual external queries
- Encourages communication via
@apiand events, not DOM scraping
Implications:
- Global page CSS should not be your primary styling strategy for component internals
- Parent components should not rely on
querySelectorinto a child’s private DOM for business logic - Use
this.template.querySelectorinside the component to access its own template elements when needed
Lightning Web Security / Locker-related constraints reinforce isolation between namespace components. Exam language often frames this as encapsulation and security of the component boundary.
Base Lightning Components and SLDS
Base Lightning components (lightning-*) wrap SLDS and platform behavior:
- Inputs:
lightning-input,lightning-combobox,lightning-textarea - Data:
lightning-datatable,lightning-tree-grid - Record UI:
lightning-record-form,lightning-record-view-form,lightning-record-edit-form,lightning-input-field - Layout/chrome:
lightning-card,lightning-layout,lightning-tabset - Feedback:
lightning-spinner, toast viaShowToastEvent
Why exams prefer them: accessibility hooks, SLDS consistency, less custom CSS, and tighter platform integration (especially record forms with FLS-aware fields).
You may still apply SLDS utility classes in markup (slds-grid, slds-p-around_medium) for layout. Prefer utilities + base components over reinventing Salesforce look-and-feel with ad-hoc CSS.
Composition and Lifecycle (High-Yield)
- Nest components as custom elements:
<c-child-panel></c-child-panel> - Pass data down with
@api - Send data up with
CustomEvent(12.4) - Lifecycle hooks such as
connectedCallback,disconnectedCallback, andrenderedCallbackappear in scenarios about initialization and cleanup—use them carefully (renderedCallbackcan run often; avoid infinite loops)
connectedCallback() {
// runs when component is inserted into the DOM
}
Common Exam Traps
| Trap | Correction |
|---|---|
Forgetting js-meta.xml targets | Component never shows in App Builder |
Using @api for private temporary state | Use internal fields instead |
| Expecting in-place array mutation to always refresh UI | Reassign arrays/objects |
| Styling parent page from component CSS | Shadow DOM scopes styles |
Building custom HTML tables when lightning-datatable fits | Prefer base components |
Confusing Aura v. / c. expressions with LWC {field} bindings | Different template languages |
Minimal End-to-End Picture
- Author html/js/css/meta bundle
- Expose with
isExposed+ targets - Accept
recordId/ config via@api - Load data with
@wireor imperative Apex (12.4) - Render with base components + SLDS utilities
- Communicate with events and public properties
Bottom line: LWC fundamentals are four key files, decorators (@api, reactivity/@track, @wire), Shadow DOM encapsulation, base Lightning components + SLDS, and meta.xml exposure/targets. Master these before drilling data access patterns in the next section.
Which files are part of a typical Lightning Web Component bundle?
What is the primary purpose of the @api decorator in LWC?
A developer builds an LWC but cannot find it in the Lightning App Builder component list for a record page. Which meta configuration issue is the most likely cause?