Lightning Web Component Framework
The Lightning Web Component (LWC) Framework is the modern UI framework Salesforce ships for building custom components on the Lightning Platform.
Definition
The Lightning Web Component (LWC) Framework is the modern UI framework Salesforce ships for building custom components on the Lightning Platform. LWC is built directly on top of web standards: Web Components, ES Modules, Shadow DOM, custom elements, and standard HTML templates. Components are written in JavaScript with a small set of decorators (api, track, wire) and a thin runtime layer that handles reactivity, security, and access to Salesforce data.
LWC replaces the older Aura Component framework as the primary path for new custom UI on Salesforce. Aura still exists, still runs, and still ships in some standard pages, but every Salesforce-built UI is moving to LWC and every new tutorial in the developer docs uses LWC. The framework runs in the browser inside a Locker Service or Lightning Web Security sandbox that prevents one component from reading another component's DOM or globals. That isolation is what lets Salesforce drop third-party components from AppExchange into a customer's org without exposing the rest of the page.
How the Lightning Web Component framework works under the hood
Why LWC exists and what it replaced
Aura, the framework LWC replaced, was designed in 2014 before browsers had native support for component systems. Aura had its own DOM, its own templating syntax, its own JavaScript event system, and its own server-side renderer. When Web Components landed in browsers natively, Salesforce rewrote the framework to sit on top of the standard rather than alongside it. The benefit is that LWC code is closer to React or Vue than to anything Aura did. The downside is that orgs still maintain Aura code and the two frameworks have to coexist on the same page.
The component anatomy: HTML, JS, CSS, metadata
Every Lightning Web Component is a bundle of files inside a folder. The folder name is the component name. Inside it, four files matter: componentName.html holds the template, componentName.js holds the JavaScript class, componentName.css holds scoped styles, and componentName.js-meta.xml holds the metadata Salesforce uses to surface the component in the App Builder. The metadata file declares whether the component can be placed on record pages, app pages, Experience Cloud sites, Flow screens, and which API version it targets.
Decorators: api, track, wire
The framework exposes three primary decorators. @api marks a property or method as part of the public interface, so parent components can pass values in or call methods. @track was needed in older API versions to mark nested objects reactive, but since API 47 it is only required for arrays and objects whose internal mutations need to trigger re-renders. @wire connects a property or method to a Salesforce data service, like a SOQL query, an Apex method, or a record load, and re-runs the function when its parameters change.
Talking to Apex from LWC
LWC calls Apex through imperative method calls or wire adapters. Imperative calls are async functions that return a Promise: you call them inline, handle the result with .then or await, and catch errors yourself. Wire adapters bind an Apex method to a property and re-run it automatically when the inputs change. The Apex method must be marked @AuraEnabled(cacheable=true) for wire, and @AuraEnabled alone for imperative. Cacheable wire calls are stored in the Lightning Data Service cache, which is shared across components on the same page.
Lightning Data Service: the local cache
LWC ships with a record-aware client cache. When you call getRecord, createRecord, updateRecord, or deleteRecord, the framework reads or writes through the Lightning Data Service. Two components on the same page reading the same record share the cache entry, and any update from one triggers a re-render in the other. This is the most useful framework feature and the one most developers underuse, defaulting instead to custom Apex methods that bypass the cache and force redundant queries.
Shadow DOM, slots, and styling
Every LWC renders inside a Shadow DOM by default. CSS in the component file is scoped to the component and cannot leak out. Global stylesheets from the host page cannot leak in. Slots let parent components pass child markup into named regions of the template, similar to React children. Native shadow makes encapsulation rigorous but breaks some testing libraries and accessibility tools. Salesforce added synthetic shadow as a compatibility layer when the platform launched, with native shadow now the default for new components targeting recent API versions.
Lightning Web Security and Locker Service
LWC runs inside a security sandbox that wraps the browser APIs. The original sandbox, Locker Service, used proxies and tight namespaces to prevent cross-component DOM access. The new sandbox, Lightning Web Security, replaces Locker with a per-namespace JavaScript runtime that is faster, less restrictive, and better aligned with modern web APIs. Orgs migrate from Locker to LWS through the Lightning Web Security setting in Session Settings. The migration occasionally breaks legacy libraries that depended on Locker idiosyncrasies.
Building a component with the LWC framework
Building a Lightning Web Component starts in a Salesforce DX project with a scratch org or sandbox. The Salesforce CLI scaffolds the component bundle, you write the HTML, JS, and metadata, deploy with sf project deploy start, and surface the component in the App Builder.
- Create a Salesforce DX project
Install the Salesforce CLI, then run sf project generate to scaffold a project. Authorize the target org with sf org login web. This sets up the folder structure expected by LWC: force-app/main/default/lwc.
- Generate the component bundle
Run sf lightning generate component --type lwc --name myComponent. The CLI creates the four-file bundle: HTML template, JS class, meta XML, and an empty CSS file. The bundle goes into force-app/main/default/lwc/myComponent.
- Mark the metadata so the component surfaces in App Builder
Open myComponent.js-meta.xml and set isExposed to true. Add the lightning__RecordPage, lightning__AppPage, or lightning__HomePage targets depending on where the component should appear. Without this metadata, the component compiles but never shows up in the Builder palette.
- Write the template and class
Edit myComponent.html with the markup. Use lwc:if, lwc:elseif, and lwc:for directives for conditional and list rendering. Edit myComponent.js to extend LightningElement, add reactive properties with @api or default reactivity, and import wire adapters or imperative Apex.
- Deploy to the org
Run sf project deploy start --source-dir force-app/main/default/lwc/myComponent. The CLI compiles, validates, and pushes the component. Deployment errors surface as compile errors or metadata mismatches, both readable in the CLI output.
- Drop the component on a Lightning page
Open Setup, Lightning App Builder, edit a record page, drag the component from the Custom palette, save, and activate. The component now renders for every user with access to the page.
Boolean flag in the meta XML that decides whether the component is visible in App Builder. Set to true for any component intended for declarative use.
List of contexts where the component can run: lightning__RecordPage, lightning__AppPage, lightning__HomePage, lightningCommunity__Page, lightning__FlowScreen, and others.
Declares which Salesforce API version the component targets. Older API versions follow the legacy reactivity rules, newer versions assume native shadow and the modern decorators behavior.
Per-org setting in Session Settings that enables the modern security sandbox, replacing the legacy Locker Service for namespaces opted in.
Set @AuraEnabled(cacheable=true) on Apex methods used in @wire. Without it, the wire adapter falls back to imperative semantics and loses cache benefits.
- @api properties are public. Renaming an @api property is a breaking change for every parent that consumes it. Treat @api as a contract.
- Wire adapters do not call .catch like a promise. Errors arrive as the error property of the wire result. Forgetting this leads to silently swallowed exceptions.
- Lightning Data Service caches by record ID and field list. Two components asking for different field subsets of the same record cause two cache entries, not one.
- Native shadow blocks global CSS, including SLDS overrides. If your design system tokens are not appearing inside an LWC, you are probably running native shadow and need to import the styles via the lightning-stylesheet pattern.
- Lightning Web Security is not a drop-in replacement for Locker. Test third-party JavaScript libraries in a sandbox before flipping LWS on at the org level.
Trust & references
Straight from the source - Salesforce's reference material on Lightning Web Component Framework.
- Lightning Web SecuritySalesforce Help
Hands-on resources to go deeper on Lightning Web Component Framework.
About the Author
Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.
Test your knowledge
Q1. What is required before deploying Lightning Web Component Framework-related code to production?
Q2. Where would a developer typically work with Lightning Web Component Framework?
Q3. What skill set is typically needed to work with Lightning Web Component Framework?
Discussion
Loading discussion…