Skip to content
svelte-vitals
English
Esc
navigateopen⌘Jpreview
On this page

correctness/orphan-lifecycle · Lifecycle call outside component initialisation

onMount, getContext and friends called outside component initialisation throw lifecycle_outside_component at runtime.

Severity: critical · Category: correctness

What it checks

Flags calls to Svelte’s lifecycle and context functions (onMount, onDestroy, beforeUpdate, afterUpdate, createEventDispatcher, getContext, setContext, hasContext, getAllContexts, value-imported from svelte, aliases and namespace imports included) that are guaranteed to run outside component initialisation:

  • at module scope in a .svelte.ts/.svelte.js runes module or a .svelte <script module> block,
  • in the constructor of a class instantiated at module scope (same file),
  • in a SvelteKit load function, form action, endpoint or hooks handler, or the init hook, or at the top level of such a file. The classic trap is getContext inside load.

Not flagged:

  • Calls inside ordinary functions, since a component may legally call them during its own initialisation.
  • createContext(): module-scope creation is the official pattern of the new context API.
  • Non-context svelte exports (mount, tick, …), same-named functions imported from other modules, factory functions/IIFEs/cross-file classes, and svelte/legacy’s createBubbler.

A function defined inside a load/handler/init body is treated as running there and inherits the flag. If you deliberately return such a closure for a component to call during its own initialisation, add svelte-vitals-disable-next-line correctness/orphan-lifecycle.

Why it matters

These functions require an active component context. Called without one, getContext/setContext/hasContext/getAllContexts throw Svelte’s lifecycle_outside_component error at runtime in every environment. The compiler compiles all of these patterns without a warning, so the failure only surfaces when the code path runs (in a load function: a 500 on every visit to that route).

onMount/beforeUpdate/afterUpdate/createEventDispatcher throw the same error in the browser. But in a Kit module that only ever runs on the server (+page.server.ts, +server.ts, hooks.server.ts) they never reach the browser at all, so the call is a silent no-op instead: no crash, nothing happens. onDestroy is the odd one out even there: it has no component-context guard of its own, so calling it still crashes, just with a plain TypeError instead of lifecycle_outside_component (still a 500 on every request if it’s in a load/handler). In a +page.ts/+layout.ts universal module or in component-scoped code, the same code also runs in the browser, where all nine throw lifecycle_outside_component.

How to fix

import { getContext } from 'svelte';

export async function load({ fetch }) {
  const user = getContext('user'); // ❌ lifecycle_outside_component — load is not component init

  return { user: await (await fetch('/api/user')).json() }; // ✅ return data instead
}

Move the call into component initialisation:

<script>
  import { setContext } from 'svelte';

  let { data } = $props();
  setContext('user', () => data.user); // ✅ component init — legal
</script>

For shared modules, expose a setup function that components call during init instead of running lifecycle calls at module scope.

Mode differences

None. This rule reads source, the same .svelte and .ts files, everywhere it runs. The CLI, the Vite plugin’s build pass, and the live dashboard’s static baseline all report it identically, and the rendered-HTML pass never re-evaluates it. Scoping a run with --route skips it: component-scoped rules have no route to attribute a finding to.

Disabling

Silence a single occurrence with <!-- svelte-vitals-disable-next-line correctness/orphan-lifecycle --> on the line above it, or turn the rule off:

export default {
  rules: {
    'correctness/orphan-lifecycle': 'off'
  }
};