disconnectedCallback() is where a Lit component cleans up after itself. This lesson covers exactly when it fires and the cleanup patterns every long-lived component should follow.
When disconnectedCallback Runs
disconnectedCallback() fires whenever your element is removed from a live document — including when its parent is removed, or when it's temporarily detached and moved elsewhere in the DOM. Like connectedCallback, this is a native Custom Elements hook that Lit extends, not something Lit invents.
Because a component can be disconnected and later reconnected (its connectedCallback firing again), cleanup logic here should be symmetrical with whatever setup happened in connectedCallback — every subscription, timer, or listener started there should be torn down here.
The WebSocket opened in connectedCallback is fully closed and its listener removed in disconnectedCallback, preventing a leaked open connection.
disconnectedCallback Signature
disconnectedCallback() {
super.disconnectedCallback(); // required — runs Lit's own teardown
// your cleanup: clear timers, unsubscribe, close connections, remove global listeners
}
Always call super.disconnectedCallback() — it performs Lit's own internal teardown.
This hook can run more than once per instance if the element is repeatedly connected and disconnected.
Any global (window/document) listeners must be removed manually here — they aren't automatically cleaned up.
Component-internal template event listeners (bound with @click etc.) are handled automatically by Lit and don't need manual removal.
Cleanup Checklist
Pair every connectedCallback side effect with its disconnectedCallback counterpart.
Started in connectedCallback
Cleaned up in disconnectedCallback
setInterval / setTimeout
clearInterval / clearTimeout
window.addEventListener(...)
window.removeEventListener(...)
new ResizeObserver(...).observe(el)
resizeObserver.disconnect()
store.subscribe(callback)
The unsubscribe function returned by subscribe
new WebSocket(...)
socket.close()
new AbortController() for a fetch
controller.abort()
Using AbortController for In-Flight Requests
If a component fetches data and is removed from the page before the request finishes, setting a reactive property after disconnection can cause a warning or a subtle bug. Using an AbortController lets you cancel the in-flight request cleanly in disconnectedCallback.
The catch block explicitly ignores AbortError, since that's an expected outcome of cleanup, not a real failure.
Designing Cleanup That's Safe to Repeat
Because connectedCallback/disconnectedCallback can each run multiple times for one instance, cleanup code should be safe to call even if setup didn't fully complete (for example, if a subscription reference is still undefined). Guard cleanup calls defensively rather than assuming setup always ran first.
disconnectedCallback() {
super.disconnectedCallback();
this.#unsubscribe?.(); // safe even if never subscribed
this.#resizeObserver?.disconnect(); // safe even if never created
}
Common Mistakes
Forgetting super.disconnectedCallback(), which can interfere with Lit's own internal teardown.
Leaving a setInterval, WebSocket, or subscription running after the component is removed, causing a memory or resource leak.
Not guarding cleanup calls, causing an error if disconnectedCallback runs before the corresponding setup ever completed.
Setting a reactive property from an async callback (like a fetch) after the component has already disconnected, without checking or aborting first.
Key Takeaways
disconnectedCallback() fires whenever the element is removed from a live document, and can fire more than once per instance.
Always call super.disconnectedCallback() when overriding it.
Every subscription, timer, listener, or open connection started in connectedCallback needs matching cleanup here.
Use AbortController to cancel in-flight fetches cleanly when a component is removed mid-request.
Pro Tip
Treat connectedCallback/disconnectedCallback as a matched pair you write together, not separately — writing the cleanup line immediately after the setup line (even before testing) makes it far less likely you'll forget it later.
You've completed the full lifecycle series. Next, learn how Lit components handle DOM events.