SfSkills Domains Docs GitHub

Home / lwc / lifecycle-hooks

lifecycle-hooks

Use when building or reviewing Lightning Web Components — specifically lifecycle. Triggers: 'LWC', 'connectedCallback', 'renderedCallback', 'memory leak', 'NavigationMixin', 'wire'. Triggers: 'LWC', 'connectedCallback', 'renderedCallback', 'memory leak', 'NavigationMixin', 'wire'. Also: 'disconnectedCallback', 'errorCallback', 'render()', 'multiple templates', 'hook order', 'parent child lifecycle', 'infinite rerender', 'component subscribes twice'. NOT for Aura components — use lwc/navigation-and-routing.

Open the skill folder on GitHub


You are a Salesforce expert in Lightning Web Component lifecycle design. Your goal is to build LWCs that are memory-safe, performant, accessible, and aligned with Salesforce runtime constraints.

Before Starting

Check for salesforce-context.md in the project root. If present, read it first — particularly whether Experience Cloud is involved, whether the org uses standard Salesforce LWC, and whether third-party libraries are approved.

Gather if not available:

Questions to Ask Before Configuring

Ask these before writing a hook. Each one maps to a failure in references/gotchas.md that only appears after the component has shipped.

AskWhy it mattersWhat a good answer adds
"Can this component be removed and re-inserted without a page reload — behind an lwc:if, in a reorderable list, in a console tab?"connectedCallback() fires again on every re-insertion; unguarded setup accumulatesThe guard shape: if (!this.subscription), and a matching teardown
"What does this component set up that lives outside its own template — a window/document listener, a timer, a message-channel subscription, a refresh handler?"The framework cleans up only the listeners it owns from the template; everything else is yoursThe exact disconnectedCallback() body, one line per setup line
"Does anything here need the rendered DOM — measuring, focusing, handing a container to a library?"DOM work is only valid in renderedCallback(), which runs after every renderA boolean guard and the one-time block behind it
"Which fields does renderedCallback() write to?"Every field is reactive; writing one there marks the component dirty and re-enters the hookEither "none" or a guard that makes the write happen once
"Where should a failure in this subtree stop — at this component, its container, or the app shell?"errorCallback() catches descendants only, and the throwing child is unmountedThe boundary's position and what its fallback template shows
"Are the two views different enough to need two HTML files, or is this an lwc:if?"render() is a real fork with its own CSS-naming and this.refs consequencesA decision, plus the second template's filename if you take the fork
"How will we know the cleanup works — what test removes the element and asserts?"Leaks are invisible in a demo and expensive in a console sessionA Jest test that calls removeChild and asserts the teardown ran

What a proper lifecycle design adds over just writing the hooks: the component survives being re-inserted, leaves nothing behind when it is removed, renders a finite number of times, and fails inside a boundary instead of blanking the page.

How This Skill Works

Mode 1: Build from Scratch

  1. Define the public API and the data contract first.
  2. Choose the lightest data pattern that fits: LDS, wire adapter, then Apex if needed.
  3. Plan what belongs in each lifecycle hook before writing code.
  4. Design loading, data, and error states together.
  5. Use Salesforce-native navigation, notifications, and static resource loading patterns.

Mode 2: Review Existing

  1. Check connectedCallback and disconnectedCallback for cleanup symmetry.
  2. Verify renderedCallback is guarded and not mutating reactive state blindly.
  3. Confirm @api props are treated as immutable inputs.
  4. Check wire handlers for both success and error branches.
  5. Flag window.location, global DOM access, alert(), or raw external script tags.

Mode 3: Troubleshoot

  1. If the component leaks or misbehaves after navigation, inspect listener and timer cleanup.
  2. If it rerenders endlessly, inspect renderedCallback and any reactive mutation it triggers.
  3. If data appears stale or blank, compare the chosen data pattern with the actual use case.
  4. If the UI breaks only in mobile or Experience Cloud, inspect navigation and CSP assumptions.
  5. Fix the lifecycle contract first, then the cosmetic issue.

LWC Lifecycle Rules

Documented order

The guide states the direction for each hook individually. Read the table as: for one parent with one child, in this order.

#StepDirectionSource
1constructor()parent → childcreate-lifecycle-hooks-created L4085
2Public properties assigned—create-lifecycle-hooks-created L4085
3Wire property seeded with { data: undefined, error: undefined }—data-wire-service-about L6468
4connectedCallback()parent → childcreate-lifecycle-hooks-dom L4101
5render()—data-wire-service-about L6469
6renderedCallback()child → parentcreate-lifecycle-hooks-rendered L4127
—disconnectedCallback() on removalparent → childcreate-lifecycle-hooks-dom L4101

The reversal at step 6 is the one people get wrong: a parent's renderedCallback runs after its children have rendered, which is why parent-level measurement works there and child-level querying in the parent's connectedCallback does not.

Hook Reference

HookUse ForAvoidGuide constraint
constructor()Cheap local state onlyDOM access; reading @api values; async work; adding host attributessuper() must be the first statement, with no parameters (L4087)
connectedCallback()Environment work: listeners, subscriptions, kicking off a fetchQuerying child elements — they do not exist yet (L4112)Can fire more than once (L4111); is synchronous, never async (L4117)
renderedCallback()One-time DOM work behind a guardWriting reactive fields; updating a wire config objectRuns after every render (L4136); wire-config writes here loop (L6408)
disconnectedCallback()Undo exactly what connectedCallback() didBusiness logic that should have happened earlierAlso synchronous (L4117)
errorCallback(error, stack)Boundary for descendant failuresTreating it as a general try/catchDescendants only; misses programmatically attached handlers (L4158, L4167)
render()Returning a different imported templateUsing it as a lifecycle notificationNot a hook — a protected method that must return a template reference (L4153)

High-Value Patterns

#

  1. Inventory the hooks. List every hook the component implements and, for each, one sentence saying what it does. Anything that cannot be described in one sentence is doing another hook's job.
  2. Answer the Questions table above. The re-insertion question and the "what lives outside the template" question decide most of the design.
  3. Place the work. Local state → constructor(). Environment and subscriptions → connectedCallback(). DOM and library initialisation → guarded renderedCallback(). Teardown → disconnectedCallback(), one line per setup line. Descendant failures → an errorCallback() boundary one level up. Template fork → render() only if two HTML files are genuinely warranted.
  4. Build from references/code-examples.md. Copy the caseBoundary + caseWatchList pair and the .js-meta.xml; reference templates/lwc/component-skeleton/ for the surrounding shell and templates/lwc/jest.config.js for mock wiring.
  5. Run the checker. python3 skills/lwc/lifecycle-hooks/scripts/check_lwc_lifecycle.py --manifest-dir force-app/main/default/lwc — zero ERRORs before review, --strict in CI.
  6. Prove it with tests. Port the three assertions from references/code-examples.md: hook order across parent and child, cleanup after document.body.removeChild, and errorCallback receiving a child's throw.
  7. Verify in the org. Toggle the component's region and confirm exactly one live subscription and one pass through the guarded renderedCallback block, per the verification steps in references/code-examples.md.

Review Checklist

Salesforce-Specific Gotchas

GotchaWhat to do
window.location breaks across Salesforce containersUse NavigationMixin so Lightning Experience, mobile, and Experience Cloud stay consistent.
alert() is not acceptable UX in LightningUse ShowToastEvent or deliberate inline UI states.
Lightning Web Security isolates DOM boundariesReaching into child shadow DOM is brittle and often blocked.
renderedCallback runs after every renderGuard reactive-state changes inside it; otherwise you create a rerender loop.
@api values are input contracts, not mutable local stateClone them before changes.
External script loading fails silently without handlingAlways catch loadScript or loadStyle failures.
connectedCallback is not a one-time initializerGuard every subscription; re-insertion calls it again.
A hook marked async runs its tail at an unpredictable pointKeep the hook synchronous and call an async method from it.

Proactive Triggers

Surface these WITHOUT being asked:

PatternSeverityWhy / Fix
Global event listeners with no cleanupCriticalMemory leak and Experience Cloud bug magnet.
renderedCallback with no guardHighInfinite rerender risk.
window.location or raw anchor hacks for navigationHighUse NavigationMixin.
document.querySelector() or child shadow DOM accessHighViolates component isolation.
External <script> tags or remote JS assumptionsCriticalUse Static Resources.
Data path with no error stateMediumBlank components are an operational failure.
super() not first in the constructorCriticalBreaks the prototype chain before this is usable.
subscribe() in connectedCallback with no null checkHighDuplicate subscriptions on every re-insertion.

Output Artifacts

When you ask for...You get...
New component scaffoldLifecycle-safe JS and HTML guidance with state handling
LWC reviewFindings on leaks, navigation, DOM isolation, and async state design
Troubleshoot memory leakRoot cause plus cleanup fix
Deployable exampleThe caseBoundary + caseWatchList bundles, .js-meta.xml, package.xml, and Jest tests from references/code-examples.md

Reference Files

FileRead it when
references/code-examples.mdYou are writing the component: full bundle, .js-meta.xml, package.xml, deploy order, and the three hook-order / cleanup / boundary Jest tests
references/gotchas.mdA hook is firing at the wrong time, more often than expected, or not at all
references/llm-anti-patterns.mdReviewing generated LWC code, or self-checking your own output
references/examples.mdYou want the shorter single-component patterns: wire + listener + guard, @api cloning, static-resource loading
references/well-architected.mdMapping the review to Well-Architected pillars, or checking which official source backs a claim
scripts/check_lwc_lifecycle.pyAuditing an existing lwc/ folder: --manifest-dir <dir>, add --strict in CI

Package files