Org Skills

salesforce-frontend-architecture

Architect-level guidance for Salesforce UI solutions — choosing between declarative (Flow, Dynamic Forms, App Builder), LWC, Experience Cloud (LWR vs Aura), Lightning Out / LWC on external sites, and off-platform apps (React/Angular) talking to Salesforce via API; org-wide component library and design system strategy; performance and governor-limit-aware UI data patterns; state management across LWCs; packaging (unlocked/2GP), DevOps, and Aura-to-LWC migration.

Download .zip Raw Source
When agents use itUse this whenever the user asks how to architect a Salesforce app UI, whether to build in LWC or externally, how to structure a Salesforce frontend codebase, how to share components across teams/orgs, how to migrate from Aura or Visualforce, or how to design Experience Cloud sites. Also apply it when reviewing Salesforce UI designs.

Install

Copilot (VS Code, Visual Studio, Copilot CLI and github.com) reads skills from the repository — commit them so the whole team gets them.

npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture -a github-copilot
# or with the org installer (adds .github/skills/salesforce-frontend-architecture):
npx -y github:AGCO-Global/org-skills add skill salesforce-frontend-architecture

Uses the open skills CLI. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, OpenCode, Windsurf and 60+ others — it asks which agent to install into.

npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture
# user-level instead of project-level:
npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture -g

Installs the salesforce-lwc-skills plugin, which bundles all Salesforce / LWC skills and keeps them updated.

/plugin marketplace add AGCO-Global/org-skills
/plugin install salesforce-lwc-skills@org-skills
# or just this skill, in this repository:
npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture -a claude-code

Use Download .zip above, then upload it under Customize → Skills → Upload skill. Team and Enterprise admins can sync this repo as a plugin marketplace instead.

Installs into .agents/skills/, which Codex reads.

npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture -a codex

Installs into Cursor's skills folder.

npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture -a cursor

Installs into Gemini CLI's skills folder.

npx skills add AGCO-Global/org-skills --skill salesforce-frontend-architecture -a gemini-cli

Any tool with rules, instructions or custom prompts: use Copy SKILL.md above and paste it in. It is plain Markdown.

Commands use your normal git sign-in to GitHub, so they work while the repository is private. Node.js 20+ required.

Skill contents

Salesforce Frontend Architecture

The first architectural question on Salesforce is not "which framework" but "how much code at all?" The platform rewards declarative-first, and every custom component is something the org must maintain through three releases a year.

1. Build-vs-configure ladder

Walk the requirement down this ladder; stop at the first rung that fits.

  1. Standard UI + page layout / Dynamic Forms / Dynamic Actions — field visibility rules, conditional sections, record pages.
  2. Screen Flows — guided multi-step processes, wizards, approvals. Combine with LWC in Flow for one custom step rather than rebuilding the wizard in code.
  3. Lightning App Builder with standard + AppExchange components — dashboards, related lists, tabs.
  4. Custom LWC on-platform — bespoke UX, complex interaction, performance-critical lists, integrations surfaced in UI.
  5. Experience Cloud (LWR) — external users, portals, public sites.
  6. Off-platform app (React/Angular/Next.js) calling Salesforce via REST/GraphQL API, Pub/Sub API, or Data Cloud — when UX requirements exceed what Lightning can render (highly branded consumer apps, heavy real-time, non-Salesforce identity), or when the majority of data lives elsewhere.
  7. Lightning Out / LWC in external containers — legacy; use only for embedding a few components in an existing web property.

Document the chosen rung and why the lower rungs were rejected. Architects get challenged on this; the written rationale is the answer.

2. On-platform codebase structure

force-app/main/default/
├── lwc/
│   ├── <feature>*/            # naming: <domain><Component> e.g. orderList, orderDetail
│   ├── shared*/               # sharedUtils, sharedStyles, sharedConstants — service-only components
│   └── ds*/                   # design-system wrappers (dsButtonBar, dsPageHeader)
├── classes/
│   ├── <Domain>Controller.cls # @AuraEnabled façade for LWC — thin, no logic
│   ├── <Domain>Service.cls    # business logic, sharing decisions
│   ├── <Domain>Selector.cls   # SOQL (fflib-style), USER_MODE
│   └── *Test.cls
├── messageChannels/           # LMS channels
├── customLabels/, staticresources/, flexipages/, flows/
  • Controller/Service/Selector separation keeps @AuraEnabled methods to one line each and lets services be reused by Flow (@InvocableMethod), triggers, and batch.
  • Multi-package orgs: split into unlocked packages by domain (core-ui, sales-orders, service-console) with explicit dependencies; a design-system package that every other UI package depends on.
  • One .forceignore, one sfdx-project.json with packageDirectories per package; CI validates each package independently.

3. Component library and design system

  • A dedicated ds* / ui* component set wrapping Lightning Base Components with the org's UX patterns (page header with breadcrumbs, empty state, error panel, confirmation modal via lightning/modal, filter bar). Product teams compose from these; they don't restyle base components.
  • SLDS 2 styling hooks and a theme (Lightning Theme + branding tokens for Experience Cloud) as the single source of visual truth; no per-component colour literals.
  • Publish the library as an unlocked package with a versioned changelog; a Storybook-like showcase page (an internal Lightning app with one tab per component) so teams can see states.
  • Governance: an ADR per new shared component, a code owner group, and a deprecation policy (announce → warn via JSDoc @deprecated → remove two releases later).

4. Data and state across components

Scenario Pattern
Same record shown in several components on a page Each uses @wire(getRecord) — LDS caches once; notifyRecordUpdateAvailable after Apex mutations
Components on one page need to coordinate (filter → list → detail) Parent container owns state, children are presentational; Lightning Message Service only when components are in separate regions/apps
Cross-tab / cross-app coordination (console) LMS with APPLICATION scope + platformWorkspaceApi
Server push (record changed by another user, integration) Platform Events / Change Data Capture via lightning/empApi; fall back to polling only where empApi is unavailable
Large lists Server-side pagination in Apex Selector; lightning-datatable with enable-infinite-loading; never client-side filtering of > 2 000 rows
Reference/config data getObjectInfo, getPicklistValues, Custom Metadata via cacheable Apex; cache in a shared service component
Expensive computed views Apex cacheable with Platform Cache (Cache.Org/Cache.Session) partition for cross-user reuse

Avoid "one giant Apex call returns the whole page" — it defeats LDS caching and fails on the first governor limit. Prefer several small cacheable wires that the platform parallelises.

5. Performance and limits — UI implications

  • Lightning page load budget: aim for EPT < 3 s on record pages; measure with Lightning Usage App and the Salesforce Community Page Optimizer / Lightning Page Performance in App Builder.
  • Fewer components per page beats fewer lines per component — each LWC region is a separate render/data cycle. Collapse rarely-used sections into tabs or accordions (lazy rendered).
  • Apex per transaction: 100 SOQL, 150 DML, 10 s CPU, 6 MB heap (12 MB async) — design Selectors to aggregate in SOQL (GROUP BY) rather than in Apex loops.
  • API-heavy off-platform apps: use Composite API / GraphQL to reduce round trips, respect API request limits, cache with ETags.
  • Experience Cloud (LWR): CDN on, minimal third-party scripts, images sized, guest-user data via cacheable Apex with careful sharing.

6. Experience Cloud

  • LWR for all new sites (performance, modern LWC, no Aura); Aura templates only when a required standard component isn't on LWR yet — check the release-notes gap list and document it.
  • Guest user: everything is public — sharing rules on guest profile, WITHOUT SHARING never in guest-facing controllers, USER_MODE everywhere.
  • Authenticated external users: Login/Identity (SSO, MFA), Person Accounts or Contacts under Accounts — decide the data model early; it drives sharing sets/groups.
  • Branding via Theme Layouts + styling hooks; component visibility via audiences; SEO via pageTitle/meta on LWR pages.
  • Custom domain, CSP Trusted Sites, and CORS configured from the start.

7. Off-platform apps talking to Salesforce

  • Auth: OAuth 2.0 PKCE (public clients), JWT Bearer (server-to-server), or External Client Apps (successor to Connected Apps). Never embed a Connected App secret in a browser bundle.
  • Data: REST sobjects/query, GraphQL API for shaped reads, Composite for multi-record writes, Pub/Sub API (gRPC) for events, Bulk API 2.0 for volume.
  • Backend-for-frontend layer (Node/Java) between the browser and Salesforce to hold tokens, aggregate calls, and cache — the browser talks to your BFF, not directly to Salesforce.
  • Keep field-level logic (validation, picklists) in Salesforce metadata and read it via describe/UI API so the external UI doesn't drift from the org.
  • Treat Salesforce API limits as a shared org resource — one runaway frontend can starve integrations. Budget and monitor.

8. Migration playbooks

Aura → LWC

  1. Inventory Aura components; classify: replace-with-standard, rewrite-as-LWC, retire.
  2. Convert leaves first (LWC can live inside Aura, not vice versa).
  3. Replace aura:method/events with @api methods, CustomEvent, LMS.
  4. Replace $A.enqueueAction with @wire/imperative Apex; make Apex cacheable where possible.
  5. Retire Aura wrappers last; measure EPT before/after.

Visualforce → LWC

  1. Split by user-facing function; many VF pages become Flows + Dynamic Forms with zero code.
  2. Remaining custom UI → LWC quick actions/record page components; PDF rendering stays on VF (renderAs="pdf") or moves to a document service.
  3. Controllers → Service/Selector layers reused by LWC controllers.

9. DevOps and quality gates

  • Source-tracked development in scratch orgs (or sandboxes with source tracking); feature branches → PR → CI runs sf project deploy validate, Jest, Apex tests (≥ 85 % coverage on changed classes), PMD, ESLint, sa11y.
  • Unlocked package versions per release; promote through sandboxes; DevOps Center, Gearset, Copado, or GitHub Actions + sf CLI.
  • Release readiness: run the org's LWC Jest suite and Apex tests against each Salesforce pre-release sandbox (Spring/Summer/Winter) — API and Locker/LWS changes break things three times a year.
  • Observability: Nebula Logger (or equivalent) for Apex; console.error funnelled to a logging Platform Event from LWC; Event Monitoring for EPT trends.

10. Architecture decision record (deliver this)

# ADR-<n>: <UI decision>
## Context — user need, volumes, users (internal/external/guest), devices
## Decision — rung on the ladder; components/packages; data patterns
## Alternatives rejected — one line each, with the killer reason
## Consequences — limits watched, ops cost, migration path
## Review date

Anti-patterns to flag

  • Custom LWC rebuilding a Flow or a record page section that Dynamic Forms handles.
  • Aura or Visualforce for new work.
  • One mega-controller returning page-shaped JSON; SObjects returned raw to the browser.
  • Business logic in @AuraEnabled methods instead of a Service layer.
  • Experience Cloud on Aura template started this year without a documented reason.
  • Browser calling Salesforce APIs directly with a client secret.
  • No design-system layer — every team restyling lightning-button.
  • Skipping pre-release sandbox testing.