diff --git a/.changeset/strong-berries-fry.md b/.changeset/strong-berries-fry.md
new file mode 100644
index 000000000000..60dbb290a854
--- /dev/null
+++ b/.changeset/strong-berries-fry.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: store forked derived values
diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js
index 22526df7c1f2..8f63922ab26e 100644
--- a/packages/svelte/src/internal/client/reactivity/batch.js
+++ b/packages/svelte/src/internal/client/reactivity/batch.js
@@ -958,12 +958,15 @@ export function fork(fn) {
var batch = Batch.ensure();
batch.is_fork = true;
+ batch_values = new Map();
var committed = false;
var settled = batch.settled();
flushSync(fn);
+ batch_values = null;
+
// revert state changes
for (var [source, value] of batch.previous) {
source.v = value;
diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js
index 39e02be7649e..3bf38bf0b2a4 100644
--- a/packages/svelte/src/internal/client/reactivity/deriveds.js
+++ b/packages/svelte/src/internal/client/reactivity/deriveds.js
@@ -378,7 +378,7 @@ export function update_derived(derived) {
if (batch_values !== null) {
// only cache the value if we're in a tracking context, otherwise we won't
// clear the cache in `mark_reactions` when dependencies are updated
- if (effect_tracking()) {
+ if (effect_tracking() || current_batch?.is_fork) {
batch_values.set(derived, value);
}
} else {
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 5ece0d79b6b8..6b5e38bc6c22 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -43,7 +43,13 @@ import {
set_dev_stack
} from './context.js';
import * as w from './warnings.js';
-import { Batch, batch_values, flushSync, schedule_effect } from './reactivity/batch.js';
+import {
+ Batch,
+ batch_values,
+ current_batch,
+ flushSync,
+ schedule_effect
+} from './reactivity/batch.js';
import { handle_error } from './error-handling.js';
import { UNINITIALIZED } from '../../constants.js';
import { captured_signals } from './legacy.js';
@@ -611,7 +617,10 @@ export function get(signal) {
return value;
}
- } else if (is_derived && !batch_values?.has(signal)) {
+ } else if (
+ is_derived &&
+ (!batch_values?.has(signal) || (current_batch?.is_fork && !effect_tracking()))
+ ) {
derived = /** @type {Derived} */ (signal);
if (is_dirty(derived)) {
diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/_config.js b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/_config.js
new file mode 100644
index 000000000000..4f7ff673d6c6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/_config.js
@@ -0,0 +1,13 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ skip_no_async: true,
+ async test({ assert, target, logs }) {
+ const fork = target.querySelector('button');
+
+ fork?.click();
+ flushSync();
+ assert.deepEqual(logs, [1, 2]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/main.svelte b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/main.svelte
new file mode 100644
index 000000000000..2adb83b73515
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/main.svelte
@@ -0,0 +1,15 @@
+
+
+