diff --git a/.changeset/clean-sloths-nail.md b/.changeset/clean-sloths-nail.md
new file mode 100644
index 0000000000..9e93a3fda6
--- /dev/null
+++ b/.changeset/clean-sloths-nail.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: ensure local prop value is read during teardown
diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js
index b817ace3f0..32969139bf 100644
--- a/packages/svelte/src/internal/client/reactivity/props.js
+++ b/packages/svelte/src/internal/client/reactivity/props.js
@@ -1,4 +1,4 @@
-/** @import { Source } from './types.js' */
+/** @import { Derived, Source } from './types.js' */
import { DEV } from 'esm-env';
import {
PROPS_IS_BINDABLE,
@@ -12,6 +12,7 @@ import { mutable_source, set, source } from './sources.js';
import { derived, derived_safe_equal } from './deriveds.js';
import {
active_effect,
+ active_reaction,
get,
is_signals_recorded,
set_active_effect,
@@ -20,7 +21,7 @@ import {
} from '../runtime.js';
import { safe_equals } from './equality.js';
import * as e from '../errors.js';
-import { BRANCH_EFFECT, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js';
+import { BRANCH_EFFECT, DESTROYED, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js';
import { proxy } from '../proxy.js';
/**
@@ -348,12 +349,17 @@ export function prop(props, key, flags, fallback) {
// The derived returns the current value. The underlying mutable
// source is written to from various places to persist this value.
var inner_current_value = mutable_source(prop_value);
+
var current_value = with_parent_branch(() =>
derived(() => {
var parent_value = getter();
var child_value = get(inner_current_value);
+ var current_derived = /** @type {Derived} */ (active_reaction);
- if (from_child) {
+ // If the getter from the parent returns undefined, switch
+ // to using the local value from inner_current_value instead,
+ // as the parent value might have been torn down
+ if (from_child || (parent_value === undefined && (current_derived.f & DESTROYED) !== 0)) {
from_child = false;
was_from_child = true;
return child_value;
diff --git a/packages/svelte/tests/runtime-runes/samples/props-local-teardown/Component.svelte b/packages/svelte/tests/runtime-runes/samples/props-local-teardown/Component.svelte
new file mode 100644
index 0000000000..c324940402
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-local-teardown/Component.svelte
@@ -0,0 +1,15 @@
+
diff --git a/packages/svelte/tests/runtime-runes/samples/props-local-teardown/_config.js b/packages/svelte/tests/runtime-runes/samples/props-local-teardown/_config.js
new file mode 100644
index 0000000000..f79098edbf
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-local-teardown/_config.js
@@ -0,0 +1,19 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, logs, target }) {
+ const [btn1] = target.querySelectorAll('button');
+
+ btn1?.click();
+ flushSync();
+
+ btn1?.click();
+ flushSync();
+
+ btn1?.click();
+ flushSync();
+
+ assert.deepEqual(logs, ['init', 'teardown', 'init', 'teardown']);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/props-local-teardown/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-local-teardown/main.svelte
new file mode 100644
index 0000000000..9aceaabb77
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/props-local-teardown/main.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+{#if toggle}
+
+{/if}
+