From 582e4443dcbf85e7d8066bf424771e58bc02397b Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Wed, 18 Feb 2026 22:20:07 +0100
Subject: [PATCH] fix: ensure head effects are kept in the effect tree (#17746)
Fixes #17726
The problem was that the head effect had no "keep me around"-flag, which
it needs because its children are not guaranteed to be present
immediately - as shown in the related issue, where the child effect is
only created once async work has completed.
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---
.changeset/old-crabs-dance.md | 5 +++++
.../src/internal/client/dom/blocks/svelte-head.js | 6 ++++--
.../samples/async-derived-title-update/_config.js | 12 ++++++++++++
.../samples/async-derived-title-update/main.svelte | 10 ++++++++++
4 files changed, 31 insertions(+), 2 deletions(-)
create mode 100644 .changeset/old-crabs-dance.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-title-update/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-title-update/main.svelte
diff --git a/.changeset/old-crabs-dance.md b/.changeset/old-crabs-dance.md
new file mode 100644
index 0000000000..efb375aa2a
--- /dev/null
+++ b/.changeset/old-crabs-dance.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: ensure head effects are kept in the effect tree
diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-head.js b/packages/svelte/src/internal/client/dom/blocks/svelte-head.js
index 7c7eed24f7..7cab6c3385 100644
--- a/packages/svelte/src/internal/client/dom/blocks/svelte-head.js
+++ b/packages/svelte/src/internal/client/dom/blocks/svelte-head.js
@@ -2,7 +2,7 @@
import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from '../hydration.js';
import { create_text, get_first_child, get_next_sibling } from '../operations.js';
import { block } from '../../reactivity/effects.js';
-import { COMMENT_NODE, HEAD_EFFECT } from '#client/constants';
+import { COMMENT_NODE, EFFECT_PRESERVED, HEAD_EFFECT } from '#client/constants';
/**
* @param {string} hash
@@ -49,7 +49,9 @@ export function head(hash, render_fn) {
}
try {
- block(() => render_fn(anchor), HEAD_EFFECT);
+ // normally a branch is the child of a block and would have the EFFECT_PRESERVED flag,
+ // but since head blocks don't necessarily only have direct branch children we add it on the block itself
+ block(() => render_fn(anchor), HEAD_EFFECT | EFFECT_PRESERVED);
} finally {
if (was_hydrating) {
set_hydrating(true);
diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-title-update/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-title-update/_config.js
new file mode 100644
index 0000000000..bda2f36efb
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-derived-title-update/_config.js
@@ -0,0 +1,12 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ await tick();
+
+ const p = target.querySelector('p');
+ assert.equal(p?.innerHTML, 'hello');
+ assert.equal(window.document.title, 'hello');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-title-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-title-update/main.svelte
new file mode 100644
index 0000000000..3af3c746f1
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-derived-title-update/main.svelte
@@ -0,0 +1,10 @@
+
+
+
{value}