Every effect, and onCleanup belongs to a scope. When the scope disposes, everything inside it tears down. Most JSX boundaries create a scope for you — knowing which ones, and where that scope is missing, is the whole contract.
The table
Boundary
Auto-scoped
Disposed when
Function component body — (props) => <el/>
yes
the element is removed
Intrinsic or class element — <div/>, <MyClass/>
yes
the element is removed
Fragment — <>...</>
yes (via the JSX transform)
the fragment’s disposables run
Reactive child slot — {() => signal()}
yes
the slot updates or its parent unmounts
<For> render callback, per item
yes
the item’s key leaves each, or the list unmounts
Custom element connectedCallback
no — opt in with render
you call the returned unmount in disconnectedCallback
App root (document.getElementById("app"))
no — opt in with render
you call the returned unmount
If you write onCleanup(...) or effect(...) inside any of the first five rows, the library owns its lifetime. At the boundaries below, you own it.
render — mount with a scoped lifetime
render(target, setup) runs setup inside a detached effectScope, appends its returned node to target, and hands back a single unmount thunk. Calling unmount removes the node from the DOM, disposes its Symbol.dispose hook, and tears down every effect / onCleanup registered inside setup.
Mounting an app
Typical entry point — mount a root component into the page container and keep a reference to the unmount thunk (e.g. for hot-module replacement):
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
The getElementById() method of the Document interface returns an Element object representing the element whose id property matches the specified string. Since element IDs are required to be unique if specified, they're a useful way to get access to a specific element quickly.
getElementById("app")!,
() => <
import App
App /> as
interfaceElement
Element is the most general base class from which all element objects (i.e., objects that represent elements) in a Document inherit. It only has methods and properties common to all kinds of elements. More specific classes inherit from Element.
render runs setup inside an effectScope detached from any enclosing effect — re-running an outer effect won’t tear your scope down; only calling the returned unmount will.
Why For items get their own scope
Each <For> item runs its render callback inside its own effectScope. onCleanup registered during render fires when that item’s key leaves each — not only on full-list unmount. This keeps per-row resources (subscriptions, intervals, observers) bound to the row’s actual lifetime.