
Generating Lwc Components
Generate Salesforce Lightning Web Components with matching Apex controllers using cacheable wire methods, mutations, and enforced security patterns.
Overview
Generating LWC Components is an agent skill for the Build phase that provides Apex controller templates for Lightning Web Components with wire, security, and bulk-safe queries.
Install
npx skills add https://github.com/forcedotcom/sf-skills --skill generating-lwc-componentsWhat is this skill?
- Apex controller template with @AuraEnabled(cacheable=true) for @wire data loads
- Non-cacheable @AuraEnabled methods for create/update/delete style mutations
- WITH SECURITY_ENFORCED and sharing-aware controller skeleton
- Bulk-safe query limits and escaped search filters in SOQL examples
- Structured sections for cacheable vs mutation endpoints in one class file
- Template splits cacheable wire methods and non-cacheable mutation sections
- Default SOQL limit pattern of 50 records when limitSize is null
Adoption & trust: 745 installs on skills.sh; 513 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need LWC screens backed by Apex but keep generating controllers that break caching rules, security, or bulk query limits.
Who is it for?
Salesforce indie devs or consultants scaffolding standard list-and-detail LWCs with @wire and secured SOQL.
Skip if: Teams not on Salesforce Lightning or builders who only need Visualforce or off-platform React with no Apex.
When should I use this skill?
You are generating or extending Lightning Web Components that need a secured Apex controller with wire and DML endpoints.
What do I get? / Deliverables
You get a replaceable LwcController template with cacheable reads, mutation methods, and enforced sharing ready to wire from generated LWC files.
- Apex controller class with cacheable and mutation @AuraEnabled methods
- SOQL query patterns wired to LWC parameters (parentId, searchTerm, limitSize)
Recommended Skills
Journey fit
The skill is entirely about implementing UI on Salesforce during product construction, not distribution or ops monitoring. LWC is the customer-facing Lightning layer; the template centers @AuraEnabled controllers wired from the component front end.
How it compares
Salesforce LWC+Apex template skill—not a generic React component generator or REST API-only backend skill.
Common Questions / FAQ
Who is generating-lwc-components for?
Developers building on Salesforce Lightning who want agent-generated LWCs paired with controllers that follow platform security and wire caching conventions.
When should I use generating-lwc-components?
Use it in Build while implementing frontend screens on Salesforce—account pickers, searchable lists, and record actions—before you hand components to QA or admins.
Is generating-lwc-components safe to install?
It is code-generation guidance from forcedotcom/sf-skills; review the Security Audits panel on this page and enforce your org’s CRUD/FLS policies on any generated Apex.
SKILL.md
READMESKILL.md - Generating Lwc Components
/** * APEX CONTROLLER TEMPLATE FOR LWC * * This template demonstrates @AuraEnabled methods for LWC with: * - Cacheable methods for wire service * - Non-cacheable methods for data mutations * - Proper error handling * - Security enforcement * - Bulk-safe patterns * * Replace: LwcController → YourControllerName */ public with sharing class LwcController { // ═══════════════════════════════════════════════════════════════════════ // CACHEABLE METHODS (For @wire service) // ═══════════════════════════════════════════════════════════════════════ /** * Get records for display in LWC * Use with @wire decorator for automatic caching and refresh * * @param parentId - Parent record ID (optional filter) * @param searchTerm - Search filter (optional) * @param limitSize - Max records to return * @return List of Account records */ @AuraEnabled(cacheable=true) public static List<Account> getAccounts(Id parentId, String searchTerm, Integer limitSize) { try { Integer recordLimit = limitSize != null ? limitSize : 50; String searchKey = String.isNotBlank(searchTerm) ? '%' + String.escapeSingleQuotes(searchTerm) + '%' : '%'; return [ SELECT Id, Name, Industry, AnnualRevenue, Phone, CreatedDate FROM Account WHERE Name LIKE :searchKey WITH SECURITY_ENFORCED ORDER BY Name LIMIT :recordLimit ]; } catch (Exception e) { throw new AuraHandledException(e.getMessage()); } } /** * Get single record by ID */ @AuraEnabled(cacheable=true) public static Account getAccountById(Id accountId) { try { List<Account> accounts = [ SELECT Id, Name, Industry, AnnualRevenue, Phone, BillingAddress, (SELECT Id, FirstName, LastName, Email FROM Contacts LIMIT 5) FROM Account WHERE Id = :accountId WITH SECURITY_ENFORCED LIMIT 1 ]; if (accounts.isEmpty()) { throw new AuraHandledException('Account not found'); } return accounts[0]; } catch (Exception e) { throw new AuraHandledException(e.getMessage()); } } /** * Get picklist values for a field */ @AuraEnabled(cacheable=true) public static List<PicklistOption> getIndustryOptions() { List<PicklistOption> options = new List<PicklistOption>(); Schema.DescribeFieldResult fieldResult = Account.Industry.getDescribe(); List<Schema.PicklistEntry> entries = fieldResult.getPicklistValues(); for (Schema.PicklistEntry entry : entries) { if (entry.isActive()) { options.add(new PicklistOption(entry.getLabel(), entry.getValue())); } } return options; } // ═══════════════════════════════════════════════════════════════════════ // NON-CACHEABLE METHODS (For data mutations) // ═══════════════════════════════════════════════════════════════════════ /** * Create a new account * * @param accountData - JSON string of account fields * @return Created Account with Id */ @AuraEnabled public static Account createAccount(String accountData) { try { // Parse JSON to Map Map<String, Object> fieldMap = (Map<String, Object>) JSON.deserializeUntyped(accountData); // Create Account record Account newAccount = new Account(); newAccount.Name = (String) fieldMap.get('Name'); newAccount.Industry = (String) fieldMap.get('Industry'); newAccount.Phone = (String) fieldMap.get('Phone'); // Validate required fields if (String.isBlank(newAccount.Name)) {