From 669f6b45a3d02aa55b4011576b8d4ded2b5b7135 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:27:46 +0100 Subject: [PATCH] fix: prevent hydration error on async `{@html ...}` (#17999) Fixes #17981 --- .changeset/four-beers-like.md | 5 +++++ .../3-transform/client/visitors/shared/fragment.js | 4 +--- .../samples/async-hydrate-html-tag/_config.js | 10 ++++++++++ .../samples/async-hydrate-html-tag/main.svelte | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 .changeset/four-beers-like.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-hydrate-html-tag/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-hydrate-html-tag/main.svelte diff --git a/.changeset/four-beers-like.md b/.changeset/four-beers-like.md new file mode 100644 index 0000000000..967d76af66 --- /dev/null +++ b/.changeset/four-beers-like.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent hydration error on async `{@html ...}` diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index bd3e708662..f77af74f7e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -101,7 +101,7 @@ export function process_children(nodes, initial, is_element, context) { if (is_static_element(node)) { skipped += 1; } else if ( - node.type === 'EachBlock' && + (node.type === 'EachBlock' || node.type === 'HtmlTag') && nodes.length === 1 && is_element && // In case it's wrapped in async the async logic will want to skip sibling nodes up until the end, hence we cannot make this controlled @@ -109,8 +109,6 @@ export function process_children(nodes, initial, is_element, context) { !node.metadata.expression.is_async() ) { node.metadata.is_controlled = true; - } else if (node.type === 'HtmlTag' && nodes.length === 1 && is_element) { - node.metadata.is_controlled = true; } else { const id = flush_node( false, diff --git a/packages/svelte/tests/runtime-runes/samples/async-hydrate-html-tag/_config.js b/packages/svelte/tests/runtime-runes/samples/async-hydrate-html-tag/_config.js new file mode 100644 index 0000000000..cb73ab1833 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-hydrate-html-tag/_config.js @@ -0,0 +1,10 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['hydrate'], + async test({ assert, target }) { + await tick(); + assert.htmlEqual(target.innerHTML, `
first test