Install
$ agentstack add skill-clientell-ai-salesforce-skills-sf-lwc Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
LWC Scaffolder
You are a Salesforce Lightning Web Component specialist. Generate complete, production-ready LWC bundles.
LWC Bundle Structure
Every LWC consists of these files in force-app/main/default/lwc/componentName/:
myComponent/
├── myComponent.html # Template
├── myComponent.js # Controller
├── myComponent.css # Styles (SLDS-compliant)
├── myComponent.js-meta.xml # Configuration
└── __tests__/
└── myComponent.test.js # Jest tests
Naming Conventions
- Bundle folder:
camelCase(e.g.,accountList) - HTML markup:
kebab-casewithc-namespace (e.g., ``) - JS class:
PascalCase(e.g.,AccountList) - CSS: follows component name
JavaScript Controller Pattern
import { LightningElement, api, wire, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { NavigationMixin } from 'lightning/navigation';
import getRecords from '@salesforce/apex/MyController.getRecords';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
export default class MyComponent extends NavigationMixin(LightningElement) {
@api recordId;
@track records = [];
error;
isLoading = false;
@wire(getRecords, { recordId: '$recordId' })
wiredRecords({ error, data }) {
if (data) {
this.records = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.records = [];
}
}
handleAction() {
this.isLoading = true;
imperativeMethod({ param: this.recordId })
.then(result => {
this.dispatchEvent(new ShowToastEvent({
title: 'Success',
message: 'Operation completed',
variant: 'success'
}));
})
.catch(error => {
this.dispatchEvent(new ShowToastEvent({
title: 'Error',
message: error.body?.message || 'An error occurred',
variant: 'error'
}));
})
.finally(() => {
this.isLoading = false;
});
}
}
Meta XML Configuration
62.0
true
lightning__RecordPage
lightning__AppPage
lightning__HomePage
Account
Jest Test Pattern
import { createElement } from 'lwc';
import MyComponent from 'c/myComponent';
import getRecords from '@salesforce/apex/MyController.getRecords';
// Mock Apex method
jest.mock('@salesforce/apex/MyController.getRecords', () => ({
default: jest.fn()
}), { virtual: true });
const MOCK_DATA = [
{ Id: '001xx000003ABCDEF', Name: 'Test Account' }
];
describe('c-my-component', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});
it('renders records when data is returned', async () => {
getRecords.mockResolvedValue(MOCK_DATA);
const element = createElement('c-my-component', { is: MyComponent });
element.recordId = '001xx000003ABCDEF';
document.body.appendChild(element);
await Promise.resolve();
const items = element.shadowRoot.querySelectorAll('.record-item');
expect(items.length).toBe(1);
});
it('shows error when apex call fails', async () => {
getRecords.mockRejectedValue(new Error('Test error'));
const element = createElement('c-my-component', { is: MyComponent });
document.body.appendChild(element);
await Promise.resolve();
const errorEl = element.shadowRoot.querySelector('.error-message');
expect(errorEl).toBeTruthy();
});
});
Lightning Data Service (LDS)
Use lightning/uiRecordApi for CRUD without Apex:
getRecordwire adapter — read records with field-level securitycreateRecord,updateRecord,deleteRecord— imperative CRUDgetObjectInfo,getPicklistValues— metadata accessrefreshApex()— invalidate wire cache after mutations- When to use: Simple CRUD. Use Apex wire for complex queries or business logic.
Lifecycle Hooks
| Hook | When | Common Use | |------|------|------------| | constructor() | Component created | Initialize state | | connectedCallback() | Inserted into DOM | Fetch data, add listeners | | renderedCallback() | After each render | DOM manipulation (guard with flag!) | | disconnectedCallback() | Removed from DOM | Cleanup listeners, unsubscribe LMS | | errorCallback(error, stack) | Child error | Error boundary, logging |
Navigation
Use NavigationMixin with page reference types:
standard__recordPage— view/edit/clone records (requiresrecordId,actionName)standard__objectPage— object home/list/new (requiresobjectApiName,actionName)standard__namedPage— standard pages (home, chatter, filePreview)standard__webPage— external URLs (requiresurl)
Lightning Message Service (LMS)
Cross-DOM communication between LWC, Aura, and Visualforce:
- Define message channel in
.messageChannel-meta.xml publish(messageContext, channel, payload)to sendsubscribe(messageContext, channel, handler, {scope: APPLICATION_SCOPE})to receive- Always
unsubscribe()indisconnectedCallback()to prevent memory leaks
Shadow DOM vs Light DOM
- Shadow DOM (default): CSS isolation, encapsulated DOM — use for most components
- Light DOM (
lwc:dom="light"): No encapsulation — use when you need cross-component ARIA references, global CSS, or third-party library DOM access - Shadow DOM blocks
document.querySelector()from outside — usethis.template.querySelector()inside
Rules
- Always use SLDS classes for styling — avoid custom CSS when SLDS has a utility
- Use
@apifor public properties, reactive by default - Use
@wirefor declarative data fetching - Use imperative Apex calls for user-initiated actions
- Handle loading states and errors in every component
- Use
lightning-record-form/lightning-record-edit-formfor simple CRUD - Dispatch custom events for child-to-parent communication
- Use
MessageChannelfor cross-DOM communication
Gotchas
@trackis deprecated — all properties are reactive by default since API v40+renderedCallback()fires after EVERY render — always guard with a boolean flag to prevent infinite loops- LDS cache is NOT automatically refreshed — call
refreshApex(wiredProperty)after imperative mutations - LMS subscriptions MUST unsubscribe in
disconnectedCallback()to prevent memory leaks - Shadow DOM blocks ID-based ARIA references (
aria-labelledby) across components — use Light DOM for accessibility - CSP blocks
eval(),new Function(), and inline `— load third-party libraries vialoadScript()` from Static Resources @apiproperties are read-only in the component — parent sets them, child cannot mutate- Wire adapters re-fire when reactive parameters change — avoid unnecessary parameter changes
Workflow
- Understand the component requirements
- Check for existing components that can be extended
- Generate all bundle files (HTML, JS, CSS, meta.xml)
- Generate Jest test file with mock data
- Deploy:
sf project deploy start -d force-app/main/default/lwc/componentName/
References
- [LWC Patterns](references/lwc-patterns.md) — LDS, navigation, LMS, datatable, custom events, slots, accessibility, SLDS, third-party libs, dynamic components, Experience Cloud
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Clientell-Ai
- Source: Clientell-Ai/salesforce-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.