From f07d4da349b3930bb6d8105ad06374a86ddbc6bb Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 5 Feb 2025 13:50:11 +0100 Subject: [PATCH 1/2] fix: ensure custom element updates don't run in hydration mode When a custom element is created before Svelte hydration kicks in, it will scaffold itself, using the properties given via attributes. Now when a custom element property is set during Svelte's hydration, the Svelte custom element component could run logic like updating an each block. Without turning off hydration mode during that time, the update would try to pick up existing element nodes (because it thinks they must be there because of hydration mode), and crash. No test because it would require a setup where we can ensure the element is scaffolded before hydration runs. Fixes #15213 --- .../client/dom/elements/attributes.js | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index eab27e6c02d5..bb156d63d6b7 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -1,5 +1,5 @@ import { DEV } from 'esm-env'; -import { hydrating } from '../hydration.js'; +import { hydrating, set_hydrating } from '../hydration.js'; import { get_descriptors, get_prototype_of } from '../../../shared/utils.js'; import { create_event, delegate } from './events.js'; import { add_form_reset_listener, autofocus } from './misc.js'; @@ -213,6 +213,12 @@ export function set_custom_element_data(node, prop, value) { // or effect var previous_reaction = active_reaction; var previous_effect = active_effect; + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, + // then it might run block logic in hydration mode, which we have to prevent. + let was_hydrating = hydrating; + if (hydrating) { + set_hydrating(false); + } set_active_reaction(null); set_active_effect(null); @@ -239,6 +245,9 @@ export function set_custom_element_data(node, prop, value) { } finally { set_active_reaction(previous_reaction); set_active_effect(previous_effect); + if (was_hydrating) { + set_hydrating(true); + } } } @@ -262,6 +271,13 @@ export function set_attributes( is_custom_element = false, skip_warning = false ) { + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, + // then it might run block logic in hydration mode, which we have to prevent. + let is_hydrating_custom_element = hydrating && is_custom_element; + if (is_hydrating_custom_element) { + set_hydrating(false); + } + var current = prev || {}; var is_option_element = element.tagName === 'OPTION'; @@ -415,6 +431,10 @@ export function set_attributes( } } + if (is_hydrating_custom_element) { + set_hydrating(true); + } + return current; } From 25ceb7bc8e909a19bb518540361468094eef3a8c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 5 Feb 2025 14:12:35 -0500 Subject: [PATCH 2/2] changeset --- .changeset/light-ligers-switch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/light-ligers-switch.md diff --git a/.changeset/light-ligers-switch.md b/.changeset/light-ligers-switch.md new file mode 100644 index 000000000000..51b3271047d4 --- /dev/null +++ b/.changeset/light-ligers-switch.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure custom element updates don't run in hydration mode