LWC Conventions and Structure
Naming Conventions
Start with naming rules first (component identifiers, code symbols, and styling tokens), then move to folder/class organization.
Project code prefix
LWC bundle names use a leading camelCase segment for your project (for example acme in acmeDetail and acmeLookup). The JavaScript class is PascalCase with that segment as a word (AcmeDetail, AcmeLookup).
Why the prefix is lowercase in the bundle name (not PascalCase or ALL_CAPS): In the browser, custom components are referenced with a kebab-case tag built from the bundle name (for example acmeDetail → <c-acme-detail>). If you use capitals in the wrong places in the folder / bundle name, the tag string splits awkwardly and is hard to read in markup and DevTools—for example:
AcmeComponentName→ rendered and referenced likec-a-cme-component-nameACMEComponentName→ rendered and referenced likec-a-c-m-e-component-name
A lowercase leading segment plus camelCase (acmeDetail, acmeLookup) keeps the generated tag predictable and readable (<c-acme-detail>). Apex still uses an ALL_CAPS prefix with an underscore; only the LWC bundle's first segment stays lowercase for this HTML tagging behavior.
Composition examples in this document use exampleParent and exampleChild as bundle names so parent/child relationships are clear without implying a specific product prefix.
Components
-
Format: camelCase (typically
acmeComponentNamewith your project segment) -
Naming: Descriptive camelCase name that clearly indicates the component's purpose
-
Pattern: Component folder and files use the same camelCase name
-
Examples:
// Parent/child composition (generic bundle names) // Component folder: exampleParent/ // Files: exampleParent.js, exampleParent.html, exampleParent.css (compiled), exampleParent.js-meta.xml // Source: __styles__/exampleParent.scss (compiles to exampleParent.css) export default class ExampleParent extends LightningElement { } // Component folder: exampleChild/ // Files: exampleChild.js, exampleChild.html, exampleChild.css (compiled), exampleChild.js-meta.xml // Source: __styles__/exampleChild.scss (compiles to exampleChild.css) export default class ExampleChild extends LightningElement { }
Properties
-
Format: camelCase
-
Naming: Descriptive names that indicate purpose
-
Public Properties: Use
@apidecorator -
Private Properties: Prefix with underscore (
_) for backing properties -
Private Property Scope: Properties prefixed with
_should only be referenced within the same JavaScript file. They must not be referenced in:- Component HTML templates
- Other JavaScript files
- Parent or child components
-
Examples:
// Public properties @api recordId; @api label = 'Click Me'; @api disabled = false; // Private backing properties (for getters/setters) // These should ONLY be used within this JS file _customClass = 'default-class'; _align = 'Left'; // Private reactive properties (no underscore - used in templates) actionClass = ''; isLoading = false;
Important: Underscore-prefixed properties (_propertyName) are implementation details and should never be accessed from templates or other files. Use getters/setters or public properties for template access.
Variables
-
Format: camelCase
-
Naming: Descriptive names that clearly indicate what the variable holds
-
Meaningful Names Required: Variable names must be meaningful and self-documenting
-
Single/Double Character Variables: Not allowed except for loop index variables (
i,j,k, etc.) -
Examples:
// Good: Meaningful variable names const accountName = 'Test Account'; const recordCount = 10; const isLoading = true; const buttonElement = this.template.querySelector('button'); const accountData = await fetchAccountData(); const errorMessage = error.body?.message || 'Unknown error'; // Good: Loop index variables (exception to the rule) for (let i = 0; i < items.length; i++) { processItem(items[i]); } for (let j = 0; j < rows.length; j++) { for (let k = 0; k < columns.length; k++) { processCell(rows[j], columns[k]); } } // Bad: Single/double character variables (except loop indices) const a = 'Test Account'; // Bad - use accountName const x = 10; // Bad - use recordCount const el = this.template.querySelector('button'); // Bad - use buttonElement const d = await fetchAccountData(); // Bad - use accountData // Bad: Unclear abbreviations const accNm = 'Test Account'; // Bad - use accountName const btn = this.template.querySelector('button'); // Bad - use buttonElement const err = error.message; // Bad - use errorMessage // Good: Meaningful names even in short scopes const selectedItem = items[selectedIndex]; const currentUser = getCurrentUser(); const formData = new FormData();
Best Practices:
- Use full words, not abbreviations (unless the abbreviation is widely understood)
- Be specific about what the variable contains (e.g.,
accountNamenotname,buttonElementnotelement) - Use descriptive names even for temporary variables
- Only use single-character variables (
i,j,k) for loop indices
Methods
-
Format: camelCase
-
Naming: Verb-based names that describe the action performed
-
Event Handlers: Prefix with
handle(e.g.,handleClick,handleChange) -
Private Methods: Do not use
_prefix. The absence of@apiis the indicator that a method is private. Use descriptive verb-based names. -
Examples:
// Public methods @api focus() { this.template.querySelector('button').focus(); } // Event handlers handleClick() { // Handle click } handleInputChange(event) { // Handle input change } // Private methods setColorClasses() { // Set color classes } validateInput(input) { // Validate input }
Constants
-
Format: UPPER_SNAKE_CASE
-
Naming: Descriptive names in all caps with underscores
-
Location: Define at the top of the file, outside the class
-
Examples:
const DEFAULT_CLASS = 'example-component'; const ICON_CLASS = 'slds-button__icon slds-current-color'; const DEFAULT_COPY_TEXT = 'URL has been copied to your clipboard.'; const MAX_RETRY_ATTEMPTS = 3;
Style Naming
Apply these naming rules to styles after core JavaScript naming conventions are established.
CSS Classes
-
Format: kebab-case or BEM methodology
-
Naming: Use SLDS classes when possible, custom classes should be descriptive
-
Examples:
/* SLDS classes */ .slds-button .slds-button_brand .slds-text-align_center /* Custom classes */ .example-component .example-child-custom-color .example-parent-heading
CSS Custom Properties (Variables)
-
Format: kebab-case with component prefix
-
Naming: Use component prefix followed by descriptive name
-
Examples:
:host { --example-child-color-background: #0070d2; --example-child-color-border: #0070d2; --example-parent-text-font-size: 1rem; --example-parent-text-font-weight: 700; }
Component Structure and Organization
Read this section from outside-in: repository/package layout, then component files, then class member order.
File Organization
Each component must consist of the following files in a single folder:
componentName.js- Component JavaScript classcomponentName.html- Component templatecomponentName.css- Compiled CSS file (generated from SASS/SCSS)componentName.js-meta.xml- Component metadata__styles__/- Folder containing SASS/SCSS source files
Style Files:
- Use SASS/SCSS for component stylesheets (source files)
- SASS/SCSS source files should be placed in a
__styles__folder within the component directory - The main SASS/SCSS file should be named
componentName.scssorcomponentName.sass - Additional SASS files (mixins, variables, etc.) can be organized as partials within the
__styles__folder - SASS/SCSS files compile to CSS files that are stored in the main component directory (same level as
.js,.html,.js-meta.xml) - The compiled CSS file (
componentName.css) is what LWC uses at runtime
Example:
src/lwc/exampleParent/
├── exampleParent.js
├── exampleParent.html
├── exampleParent.css (compiled from __styles__/exampleParent.scss)
├── exampleParent.js-meta.xml
└── __styles__
├── exampleParent.scss (SASS source)
├── _variables.scss (SASS partial)
└── _mixins.scss (SASS partial)
Alternative Simple Structure:
src/lwc/exampleParent/
├── exampleParent.js
├── exampleParent.html
├── exampleParent.css (compiled from __styles__/exampleParent.scss)
├── exampleParent.js-meta.xml
└── __styles__
└── exampleParent.scss (SASS source)
Note: The componentName.css file is generated during the build/compilation process from the SASS/SCSS files in the __styles__ folder. Developers should edit the SASS/SCSS source files, not the compiled CSS files directly.
Package Organization
Organize LWC bundles in Salesforce DX paths so components are easy to locate and group by feature/domain:
force-app/
└── main/
└── default/
└── lwc/
├── account/
├── sharedButton/
└── searchPanel/
Component Class Structure Order
Components should follow this order. Method visibility matches the Apex standard: @api (public) methods appear above any method that is not @api (private). Place @wire immediately after @api properties when adapters use reactive parameters such as $recordId. @api methods follow @wire, then lifecycle hooks. Event handlers and other helpers are private and come last.
- Imports - All import statements
- Constants - Constants defined outside the class
- Class Declaration - Class definition extending LightningElement
- Private Properties - Private backing properties (prefixed with
_) - ONLY used within this JS file, never in templates - Private Reactive Properties - Private reactive properties (can be used in templates)
- Computed Properties (Getters) - Getter methods
- Public Properties -
@apiproperties - Wire Methods -
@wireproperties (typically after@apifields the wire uses) - Public Methods -
@apimethods (must appear before non-@apimethods below) - Lifecycle Hooks -
connectedCallback,renderedCallback,disconnectedCallback,errorCallback - Private Methods - All non-
@apimethods: event handlers (handle*) first, then other private helpers (no_prefix on methods; absence of@apiindicates privacy)
Example:
// 1. Imports
import { LightningElement, api } from 'lwc';
import { isEmpty } from 'c/exampleUtils';
// 2. Constants
const DEFAULT_CLASS = 'example-component';
// 3. Class Declarations
export default class ExampleParent extends LightningElement {
// 4. Private Properties
// Note: These underscore-prefixed properties should ONLY be used within this JS file
// They must NOT be referenced in templates or other files
_customClass = DEFAULT_CLASS;
_align = 'Left';
// 5. Private Reactive Properties
// Note: These properties (no underscore) can be used in templates
contentClass = '';
textClass = 'example-component';
isLoading = false;
// 6. Computed Properties (Getters)
get isHeading() {
return !this.type.includes('p');
}
// Good: Private property accessed within same file (via getter)
get align() {
return this._align?.toLowerCase() || 'left';
}
// 7. Public Properties
@api recordId;
@api label = 'Default Label';
@api get customClass() {
return this._customClass;
}
set customClass(value) {
this._customClass = value || DEFAULT_CLASS;
}
// 8. Wire Methods
@wire(getRecord, { recordId: '$recordId', fields: ACCOUNT_FIELDS })
wiredAccount({ error, data }) {
if (data) {
this.account = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.account = undefined;
}
}
// 9. Public Methods
@api focus() {
const button = this.template.querySelector('button');
if (button) {
button.focus();
}
}
// 10. Lifecycle Hooks
connectedCallback() {
this.classList.add(...this.customClass.split(' '));
}
renderedCallback() {
// DOM manipulation if needed
}
disconnectedCallback() {
// Cleanup if needed
}
// 11. Private Methods (public @api methods above; handlers first, then helpers)
handleClick() {
this.dispatchEvent(new CustomEvent('click'));
}
setColorClasses() {
// Set color classes
}
}
Component Metadata
.js-meta.xml Structure
Usage: Define component metadata in .js-meta.xml file.
Required Elements:
apiVersion- API version for the componentisExposed- Whether component is exposed for usemasterLabel- Display label for the componentdescription- Description of the componenttargets- Where the component can be usedtargetConfigs- Configuration for specific targets
Example:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>65.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Action Button</masterLabel>
<description>Reusable action button component with multiple variants and customization options.</description>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
<target>lightning__FlowScreen</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AppPage,lightning__RecordPage,lightning__HomePage">
<property name="label" type="String" label="Button Label" description="The text displayed on the button." />
<property name="variant" type="String" label="Variant" datasource="brand,neutral,destructive" description="The button variant style." />
<property name="disabled" type="Boolean" label="Disabled" description="Whether the button is disabled." default="false" />
</targetConfig>
<targetConfig targets="lightningCommunity__Default">
<property name="label" type="String" label="Button Label" description="The text displayed on the button." />
<property name="variant" type="String" label="Variant" datasource="brand,neutral,destructive" description="The button variant style." />
</targetConfig>
<targetConfig targets="lightning__FlowScreen">
<property name="label" type="String" label="Button Label" role="inputOnly" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
API Version
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
Targets
Common Targets:
lightning__AppPage- Lightning App pageslightning__RecordPage- Record detail pageslightning__HomePage- Home pageslightningCommunity__Page- Experience Cloud pageslightningCommunity__Default- Experience Cloud default pageslightning__FlowScreen- Flow screens
TargetConfigs
Usage: Define different property configurations for different targets.
Guidelines:
- Use
targetConfigsto customize properties per target - Use
datasourcefor property values with predefined options - Use
role="inputOnly"for Flow input-only properties - Provide meaningful
labelanddescriptionfor each property
Property Types:
String- Text valuesBoolean- True/false valuesInteger- Numeric valuesObject- Complex objects
Component Metadata Best Practices
- Always provide meaningful
masterLabelanddescription - Define appropriate
targetsfor component usage - Use
targetConfigsto customize properties per target - Provide helpful
descriptiontext for each property - Use
datasourcefor properties with predefined options - Keep API versions consistent
Import Organization
Import Order
Guidelines:
- Lightning Web Component imports (
lwc) - Lightning platform imports (
lightning/*) - Salesforce imports (
@salesforce/*) - Third-party library imports
- Local utility imports (
c/*)
Examples:
// 1. LWC imports
import { LightningElement, api, wire } from 'lwc';
// 2. Lightning platform imports
import { NavigationMixin } from 'lightning/navigation';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord } from 'lightning/uiRecordApi';
import { MessageContext } from 'lightning/messageService';
// 3. Salesforce imports
import getAccountData from '@salesforce/apex/AccountController.getAccountData';
import SELECTED_CHANNEL from '@salesforce/messageChannel/exampleChild__c';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
// 4. Third-party imports (if any)
// import { Chart } from 'chart.js';
// 5. Local utility imports
import { isEmpty, showToast } from 'c/exampleUtils';
import ExampleRecordUtils from 'c/exampleRecordUtils';
Import Grouping
Guidelines:
- Group imports by category
- Use blank lines between import groups
- Sort imports alphabetically within each group (optional but recommended)
Import Best Practices
- Organize imports by category
- Use consistent import ordering
- Remove unused imports
- Use named imports when possible
- Group related imports together
Change History
Version 1.0 - 2026-03-26
- Added initial version history tracking for this document.
Last Updated: 2026-03-26 Version: 1.0