LWC Code Quality
How to Use This Standard
Use this page in sequence when reviewing or authoring LWC code so guidance builds from basics to advanced concerns:
- Start with Code Style and Formatting to establish baseline readability.
- Move to Code Complexity, especially Return statement patterns, to keep logic maintainable.
- Apply Performance Optimization and Component Composition patterns for scalable implementation.
- Validate Security and Browser Compatibility before completion.
- Finish with API Version Standards and Code Maintenance for long-term upkeep.
Code Style and Formatting
Prettier Compliance
Usage: Use Prettier for code formatting (configured in project).
Guidelines:
- Run Prettier before committing code
- Ensure consistent formatting across the codebase
- Don't disable Prettier unless absolutely necessary
Indentation
Usage: Use 4 spaces for indentation (Prettier default).
Line Length
Guidelines:
- Keep lines under 100 characters when possible
- Break long lines appropriately
- Use proper indentation for continuation
Examples:
// Good: Break long lines appropriately
const result = await this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: this.recordId,
actionName: 'view'
}
});
// Good: Call Apex through a named selector or service (imports at module top in real components)
import getAccounts from '@salesforce/apex/ACME_AccountSelector.getAccounts';
const accounts = await getAccounts();
Code Formatting Best Practices
- Use Prettier for consistent formatting
- Keep lines readable (under 100 characters)
- Use proper indentation
- Format consistently across the codebase
Code Complexity
Return statement patterns
Optimize for readability and shallow nesting, not for counting return statements.
Guidelines:
- Use early returns for guard clauses -- Invalid state, missing data, or permissions checks should fail fast at the top of handlers and async functions. This is the default pattern for LWC methods.
- Multiple exits are acceptable when each return is easy to see and the function remains short enough to follow. If exits multiply or the function grows long, extract helpers rather than forcing a single exit.
- Prefer one return at the end when several branches all contribute to one final value or outcome and early returns would duplicate setup or obscure how the result is built. This is optional, not a general rule for "complex" functions.
Example: Early returns (guard clauses)
async handleSave() {
if (!this.recordId) {
this.showError('Record ID is required');
return;
}
if (!this.isEditable) {
return;
}
await this.persist();
}
Example: Single return (intertwined branching)
Use one exit at the end when nested conditions all assign into the same outcome variable:
get displayTier() {
let tier;
if (this.amount > 10000) {
if (this.segment === 'Enterprise') {
tier = 'Premium';
} else {
tier = 'High value';
}
} else {
tier = 'Standard';
}
return tier;
}
Cognitive Complexity Limits
Guidelines:
- Keep methods simple and focused
- Refactor complex methods into smaller methods
- Use early returns and guard clauses to reduce nesting (see Return statement patterns)
- Avoid deep nesting (max 4 levels)
Examples:
// Bad: Too complex
handleClick() {
if (this.url) {
if (this.copyUrl) {
this.handleCopy();
} else {
if (this.newTab) {
this[NavigationMixin.GenerateUrl]({
type: 'standard__webPage',
attributes: { url: this.url }
}).then((url) => {
window.open(url, '_blank');
});
} else {
if (REGEX_SALESFORCE.test(this.url)) {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: this.url,
actionName: 'view'
}
});
} else {
this[NavigationMixin.Navigate]({
type: 'standard__webPage',
attributes: { url: this.url }
});
}
}
}
} else {
if (this.printPage) {
window.print();
}
}
}
// Good: Refactored with early returns and helper methods
handleClick() {
if (this.url) {
this.handleUrlClick();
} else if (this.printPage) {
window.print();
}
this.dispatchEvent(new CustomEvent('actionclicked'));
}
handleUrlClick() {
if (this.copyUrl) {
this.handleCopy();
return;
}
if (this.newTab) {
this.openInNewTab();
} else {
this.navigateToUrl();
}
}
navigateToUrl() {
const navigateConfig = REGEX_SALESFORCE.test(this.url)
? this.getRecordPageConfig()
: this.getWebPageConfig();
this[NavigationMixin.Navigate](navigateConfig);
}
Method Length Guidelines
Guidelines:
- Keep methods under 50 lines when possible
- Refactor long methods into smaller, focused methods
- Each method should do one thing well
Code Complexity Best Practices
- Keep methods simple and focused
- Prefer guard clauses and early returns; multiple exits are fine when the flow stays clear (see Return statement patterns)
- Refactor complex logic into helper methods
- Avoid deep nesting
- Use descriptive method names
Performance Optimization
Lazy Loading
Usage: Use lazy loading for heavy components or data that's not immediately needed.
Guidelines:
- Load data on demand when possible
- Use
lwc:ifto conditionally render heavy components - Load third-party libraries only when needed
Examples:
// Good: Lazy load data
async loadData() {
if (!this.dataLoaded) {
this.isLoading = true;
this.data = await fetchData();
this.dataLoaded = true;
this.isLoading = false;
}
}
// Good: Conditionally render heavy component
get shouldRenderChart() {
return this.data && this.data.length > 0;
}
<!-- Good: Single element - lwc:if on the element itself -->
<c-chart-component lwc:if={shouldRenderChart} data={data}></c-chart-component>
Wire Adapter Optimization
Usage: Optimize wire adapter usage for performance.
Guidelines:
- Use
cacheable=truefor Apex methods when possible - Use reactive parameters efficiently
- Avoid unnecessary wire adapter calls
- Handle wire adapter errors appropriately
Examples:
// Good: Use cacheable for read-only data
@wire(getAccountData, { accountId: '$recordId' })
wiredAccountData({ error, data }) {
// Handle data/error
}
// Apex method with cacheable
@AuraEnabled(cacheable=true)
public static Account getAccountData(Id accountId) {
return [SELECT Id, Name FROM Account WHERE Id = :accountId];
}
Rendering Optimization
Usage: Optimize component rendering for performance.
Guidelines:
- Use
lwc:ifto conditionally render elements - Avoid unnecessary re-renders
- Use
lwc:keywithlwc:forfor efficient list rendering - Minimize DOM manipulation in
renderedCallback
Examples:
<!-- Good: Single element - lwc:if on the element itself -->
<p lwc:if={isVisible}>Content</p>
<!-- Good: Multiple elements - wrap in template -->
<template lwc:if={isVisible}>
<p>Content</p>
<p>Additional content</p>
</template>
<!-- Good: Use lwc:key for efficient list rendering -->
<ul>
<template lwc:for={items} lwc:key={item.id}>
<li>{item.name}</li>
</template>
</ul>
Performance Best Practices
- Use lazy loading for heavy components/data
- Optimize wire adapter usage
- Minimize DOM manipulation
- Use conditional rendering appropriately
- Use
lwc:keywith lists - Avoid unnecessary re-renders
- Cache expensive computations
Component Composition
Parent-Child Communication
Usage: Use events for parent-child communication.
Child Component:
// Child dispatches event
handleClick() {
this.dispatchEvent(new CustomEvent('itemclicked', {
detail: {
itemId: this.itemId,
itemData: this.itemData
},
bubbles: true,
composed: true
}));
}
Parent Component:
<!-- Parent listens to event -->
<c-child-component onitemclicked={handleItemClicked}></c-child-component>
// Parent handles event
handleItemClicked(event) {
const { itemId, itemData } = event.detail;
// Handle event
}
Public API Pattern
Usage: Expose public methods for parent components to control child behavior.
Examples:
// Child component exposes public methods
@api focus() {
const input = this.template.querySelector('input');
if (input) {
input.focus();
}
}
@api reset() {
this.value = '';
this.error = undefined;
}
// Parent component calls public methods
handleFocusChild() {
const child = this.template.querySelector('c-child-component');
if (child) {
child.focus();
}
}
Component Interaction Patterns
Guidelines:
- Use events for child-to-parent communication
- Use public methods for parent-to-child control
- Use message channels for cross-component communication
- Keep component interfaces clear and documented
Composition Best Practices
- Use events for upward communication
- Use public methods for downward control
- Use message channels for sibling communication
- Keep components loosely coupled
- Document component interfaces
- Use clear, descriptive event names
Security
XSS Prevention
Usage: Prevent Cross-Site Scripting (XSS) attacks by properly handling user input.
Guidelines:
- Never use
innerHTMLwith user input - Use LWC's built-in data binding (safe by default)
- Sanitize user input if needed
- Use
lightning-formatted-*components for safe rendering
Examples:
<!-- Good: Safe data binding -->
<p>{userInput}</p>
<!-- Good: Use lightning-formatted components -->
<lightning-formatted-rich-text value={richText}></lightning-formatted-rich-text>
// Bad: Unsafe innerHTML manipulation
renderedCallback() {
const div = this.template.querySelector('.content');
div.innerHTML = this.userInput; // UNSAFE!
}
// Good: Use safe data binding
// Template: <div>{userInput}</div>
Input Validation
Usage: Validate all user input before processing.
Guidelines:
- Validate input on both client and server side
- Provide clear error messages for invalid input
- Sanitize input when necessary
- Use type checking and validation utilities
Examples:
// Good: Validate input
handleInputChange(event) {
const value = event.target.value;
if (!this.isValidInput(value)) {
this.error = 'Invalid input';
return;
}
this.value = value;
this.error = undefined;
}
isValidInput(value) {
// Validation logic
return value && value.length > 0 && value.length <= 255;
}
Secure Data Handling
Guidelines:
- Don't expose sensitive data in component properties
- Use secure methods for data transmission
- Handle errors without exposing sensitive information
- Follow Salesforce security best practices
Security Best Practices
- Prevent XSS attacks
- Validate all user input
- Handle data securely
- Don't expose sensitive information
- Use LWC's safe data binding
- Follow Salesforce security guidelines
Browser Compatibility
Supported Browsers
Salesforce LWC supports:
- Chrome (latest 2 versions)
- Firefox (latest 2 versions)
- Safari (latest 2 versions)
- Edge (latest 2 versions)
Guidelines:
- Test components in supported browsers
- Use feature detection when needed
- Avoid browser-specific code when possible
- Use polyfills for unsupported features if necessary
Compatibility Considerations
Guidelines:
- Use standard JavaScript features
- Avoid experimental features
- Test in all supported browsers
- Use feature detection for optional features
Examples:
// Good: Feature detection
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(this.url);
} else {
// Fallback for older browsers
this.fallbackCopyToClipboard(this.url);
}
API Version Standards
API Version Selection
Usage: Use the latest API version from the project (currently 65.0).
Guidelines:
- Keep API versions consistent across components
- Update API versions as part of regular maintenance
- Don't manually set API version unless necessary
- Document any exceptions to standard API version
API Version Maintenance
Guidelines:
- Review API versions periodically
- Update to latest API version during maintenance windows
- Test components after API version updates
- Document API version changes
API Version Best Practices
- Use consistent API versions
- Update API versions regularly
- Test after API version updates
- Document API version exceptions
Code Maintenance
TODO/FIXME Comments
Usage: TODO and FIXME comments are acceptable if there is a discussion, issue, or bug created to capture that work.
Format:
// TODO: [Issue #123] Migrate to lwc:if directive
// FIXME: [Bug #456] Handle edge case when recordId is null
Bad Example:
// TODO: Fix this later
// FIXME: This doesn't work
Conditional Rendering Migration
Requirement: Any existing if:true/if:false should be migrated to lwc:if when touching the component.
Migration Guidelines:
- Single element: Place
lwc:ifdirectly on the element (no template wrapper needed) - Multiple elements: Wrap in
<template>tag withlwc:if - No logic in brackets: Convert expressions to getters
Examples:
<!-- Old syntax (should be migrated) -->
<p if:true={isVisible}>Content</p>
<lightning-button if:true={showButton} label="Click Me"></lightning-button>
<!-- New syntax (preferred) - single element -->
<p lwc:if={isVisible}>Content</p>
<lightning-button lwc:if={showButton} label="Click Me"></lightning-button>
<!-- New syntax (preferred) - multiple elements -->
<template lwc:if={isVisible}>
<p>Content</p>
<p>Additional content</p>
</template>
<!-- Old syntax with logic (should be migrated) -->
<p if:true={status === 'success'} role="status">Success</p>
<!-- New syntax - use getter instead of logic in brackets -->
<!-- JavaScript: get isSuccess() { return this.status === 'success'; } -->
<p lwc:if={isSuccess} role="status">Success</p>
Deprecation Process
Note: This topic requires further discussion and planning. The deprecation process should include:
- Documentation - Document why the code is deprecated and what to use instead
- Transition Period - Provide a grace period for migration
- Process to List Usages/Updates - Track all usages and required updates
Placeholder for Future Implementation:
/**
* @description [DEPRECATED] Use lwc:if directive instead
* @deprecated This property will be removed in version 2.0. Use lwc:if in template instead.
* @see Component documentation for migration guide
*/
@api get isVisible() {
return this._isVisible;
}
Code Maintenance Best Practices
- Address TODOs when touching components
- Migrate
if:true/if:falsetolwc:ifwhen updating components - Keep code up to date with latest LWC features
- Remove unused code
- Update documentation with code changes
- Follow deprecation process when removing features
Change History
Version 1.0 - 2026-03-26
- Added initial version history tracking for this document.
Last Updated: 2026-03-26 Version: 1.0