From 6b16b7f73fe8223ad7ab8bc629c98ba6d581f5cf Mon Sep 17 00:00:00 2001 From: Nic Polumeyv Date: Tue, 25 Aug 2026 04:35:46 -0400 Subject: [PATCH] fix: keep defaultChecked on hydrated radio inputs with spread attributes (#18701) radio buttons were missing in the type check - remove the check instead (aligns with how we do it in the compiler, see `has_default_value_attribute`). Strictly speaking not fully correct but pragmatic solution that doesn't have any impact in practise Needed by sveltejs/kit#16926. --- .changeset/keep-radio-default-checked.md | 5 ++++ .../client/dom/elements/attributes.js | 7 ++---- .../_config.js | 23 +++++++++++++++++++ .../main.svelte | 9 ++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 .changeset/keep-radio-default-checked.md create mode 100644 packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/main.svelte diff --git a/.changeset/keep-radio-default-checked.md b/.changeset/keep-radio-default-checked.md new file mode 100644 index 0000000000..b3b32afda6 --- /dev/null +++ b/.changeset/keep-radio-default-checked.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep `defaultChecked` on hydrated radio inputs with spread attributes diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 3d71abcd10..69d4dd42a8 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -291,11 +291,8 @@ function set_attributes( skip_warning = false ) { if (hydrating && should_remove_defaults && element.nodeName === INPUT_TAG) { - var input = /** @type {HTMLInputElement} */ (element); - var attribute = input.type === 'checkbox' ? 'defaultChecked' : 'defaultValue'; - - if (!(attribute in next)) { - remove_input_defaults(input); + if (!('defaultValue' in next || 'defaultChecked' in next)) { + remove_input_defaults(/** @type {HTMLInputElement} */ (element)); } } diff --git a/packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/_config.js new file mode 100644 index 0000000000..3f3134ee7a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['hydrate'], + + async test({ assert, target }) { + const [a, b, reset] = target.querySelectorAll('input'); + + // let the deferred hydration cleanup run + await Promise.resolve(); + flushSync(); + + b.checked = true; + reset.click(); + await Promise.resolve(); + flushSync(); + + assert.equal(a.defaultChecked, true); + assert.equal(a.checked, true); + assert.equal(b.checked, false); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/main.svelte new file mode 100644 index 0000000000..cb7ff0abd3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/form-default-checked-radio-spread/main.svelte @@ -0,0 +1,9 @@ + + +
+ + + +