The story of getting an "effect_orphan" error when using $effect in a class constructor in Svelte 5

The story of getting an "effect_orphan" error when using $effect in a class constructor in Svelte 5

In Svelte 5, using `$effect` inside a class constructor caused an `effect_orphan` error after deploying to Vercel. Here we introduce the cause and how to resolve it using `$effect.root`.
2026.07.12

This page has been translated by machine translation. View original

Introduction

While building an SPA with SvelteKit + Svelte 5, everything worked fine locally with pnpm dev, but after deploying to Vercel, a 500 Internal Error occurred.

Checking the browser console revealed the following error:

Error: https://svelte.dev/e/effect_orphan
    at Ee (CDll246k.js:1:1652)
    at Hn (CDll246k.js:1:23076)
    at Kn (CDll246k.js:1:23860)
    at new <anonymous> (Cy_cxSHz.js:1:1684)

This article explains the cause of this error and how to fix it.

Prerequisites & Environment

  • Svelte 5.56.4
  • SvelteKit 2.68.0
  • @sveltejs/adapter-static 3.0.10 (SPA mode)
  • Vercel (deployed as a static site)

Cause of the Error

What I Was Doing

I had implemented state management synchronized with localStorage in a .svelte.ts file using Svelte 5 Runes ($state / $effect).

src/lib/state.svelte.ts
class QuizStateManager {
  bookmarks = $state<string[]>([]);
  history = $state<Record<string, QuestionHistory>>({});

  constructor() {
    const saved = loadFromStorage();
    this.bookmarks = saved.bookmarks;
    this.history = saved.history;

    // ❌ This is the problem
    $effect(() => {
      saveToStorage({
        bookmarks: this.bookmarks,
        history: this.history,
      });
    });
  }
  // ...
}

// Instantiated at module scope
export const quizState = new QuizStateManager();

Why It Causes an Error

Svelte 5's $effect has a constraint that it can only be executed during component initialization (i.e., at the top level of a component).

In the code above, QuizStateManager is instantiated with new at module scope, so it does not belong to any component's initialization context. This is the cause of the effect_orphan error.

The Svelte official documentation also states:

$effect can only be used in component initialization or inside another effect.

Why It Worked in Dev Mode

In pnpm dev (development mode), Svelte treats this rule violation as a warning and does not stop the app from running. However, in a production build (pnpm build), this is treated as a hard error, which is why the 500 error surfaces after deployment.

Solution

Wrap with $effect.root

$effect.root is an API for creating reactive effects outside of a component's context. By wrapping $effect with it, you can create a standalone reactive scope.

src/lib/state.svelte.ts
class QuizStateManager {
  bookmarks = $state<string[]>([]);
  history = $state<Record<string, QuestionHistory>>({});

  constructor() {
    const saved = loadFromStorage();
    this.bookmarks = saved.bookmarks;
    this.history = saved.history;

    // ✅ Wrap with $effect.root
    $effect.root(() => {
      $effect(() => {
        saveToStorage({
          bookmarks: this.bookmarks,
          history: this.history,
        });
      });
    });
  }
  // ...
}

export const quizState = new QuizStateManager();

This single change resolved the 500 error on Vercel.

What is $effect.root

API Purpose
$effect Used during component initialization. Automatically cleaned up when the component is destroyed
$effect.root Used outside components. Requires manual cleanup (by calling the returned function)

$effect.root returns a cleanup function. In this case, since the lifecycle matches the entire app, explicit cleanup was not necessary. If you need to unsubscribe at a specific time, you should save and call the return value.

const cleanup = $effect.root(() => {
  $effect(() => {
    // Reactive logic
  });
});

// When no longer needed
cleanup();

Cases Where You Are Likely to Encounter the Same Pattern

The same error tends to occur in the following situations:

  • new-ing a singleton state management class at module scope
  • Calling a createXxx() factory function at module scope
  • Using $effect in library initialization code

In all cases, the underlying cause is the same: "using $effect outside of a component."

Summary

  • Svelte 5's $effect can only be used within a component initialization context
  • Using it in a module-scope class or factory results in an effect_orphan error
  • Wrapping with $effect.root resolves the issue
  • This is easy to miss in development mode and only surfaces in production builds, so be careful

Share this article