From 0a404c986380f7a9880733864229c4be9e08e098 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:51:21 +0100 Subject: [PATCH] fix: prevent async render tag hydration mismatches (#17652) If the render tag is wrapped in `$.async`, that `$.async` call already contains surrounding markers, so we must not add our own to avoid hydration mismatches. Related to #17641, fixes #17225 --- .changeset/two-places-speak.md | 5 +++++ .../3-transform/server/visitors/RenderTag.js | 4 +++- .../samples/async-render-hydration/_config.js | 11 +++++++++++ .../samples/async-render-hydration/main.svelte | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .changeset/two-places-speak.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-render-hydration/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-render-hydration/main.svelte diff --git a/.changeset/two-places-speak.md b/.changeset/two-places-speak.md new file mode 100644 index 0000000000..957377b26b --- /dev/null +++ b/.changeset/two-places-speak.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent async render tag hydration mismatches diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js index fe20e85e1b..ac671c0188 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js @@ -37,7 +37,9 @@ export function RenderTag(node, context) { context.state.template.push(...optimiser.render_block([statement])); - if (!context.state.is_standalone) { + // If the render tag is wrapped in $.async, that $.async call already contains surrounding markers, + // so we don't need to (or rather must not, to avoid hydration mismatches) add our own. + if (!optimiser.is_async() && !context.state.is_standalone) { context.state.template.push(empty_comment); } } diff --git a/packages/svelte/tests/runtime-runes/samples/async-render-hydration/_config.js b/packages/svelte/tests/runtime-runes/samples/async-render-hydration/_config.js new file mode 100644 index 0000000000..2e37b0b9ac --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-render-hydration/_config.js @@ -0,0 +1,11 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['hydrate'], + + async test({ assert, target }) { + await tick(); + assert.htmlEqual(target.innerHTML, `Count: 1 Double: 2`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-render-hydration/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-render-hydration/main.svelte new file mode 100644 index 0000000000..e26ff87567 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-render-hydration/main.svelte @@ -0,0 +1,15 @@ + + +Count: {count} + +{#snippet ssr(num: number)} + {num} +{/snippet} + +Double: {@render ssr(double)}