From b580455c9cf5c903c1726866e6808b81332480fc Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:58:37 +0200 Subject: [PATCH] fix: serialize input default values during SSR (#18733) Alternative to #18452 --------- Co-authored-by: svelte-triage-bot --- .changeset/green-inputs-ssr.md | 5 +++++ .../3-transform/server/visitors/shared/element.js | 8 ++++++++ packages/svelte/src/internal/server/index.js | 11 +++++++++-- .../samples/input-default-value/_config.js | 3 +++ .../samples/input-default-value/_expected.html | 6 ++++++ .../samples/input-default-value/main.svelte | 10 ++++++++++ 6 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 .changeset/green-inputs-ssr.md create mode 100644 packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte diff --git a/.changeset/green-inputs-ssr.md b/.changeset/green-inputs-ssr.md new file mode 100644 index 0000000000..e034d0e79a --- /dev/null +++ b/.changeset/green-inputs-ssr.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: serialize input default values during server rendering diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index b4f4facb5d..c19d760f48 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -80,6 +80,14 @@ export function build_element_attributes(node, context, transform) { ) { events_to_capture.add(attribute.name); } + } else if ( + node.type === 'RegularElement' && + node.name === 'input' && + (attribute.name === 'defaultValue' || attribute.name === 'defaultChecked') + ) { + attributes.push(attribute); + // deopt to spread at runtime, where we can handle interaction of value/defaultValue etc + has_spread = true; // the defaultValue/defaultChecked properties don't exist as attributes } else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') { if (attribute.name === 'class') { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 3d8ec5fe5e..20bef78306 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -146,8 +146,10 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) { const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0; const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0; const is_input = (flags & ELEMENT_IS_INPUT) !== 0; + const names = Object.keys(attrs); - for (name of Object.keys(attrs)) { + outer: for (let i = 0; i < names.length; i++) { + name = names[i]; // omit functions, internal svelte properties and invalid attribute names if (typeof attrs[name] === 'function') continue; if (name[0] === '$' && name[1] === '$') continue; // faster than name.startsWith('$$') @@ -163,8 +165,13 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) { if (is_input) { if (name === 'defaultvalue' || name === 'defaultchecked') { + // value/checked takes precedence over defaultValue/defaultChecked name = name === 'defaultvalue' ? 'value' : 'checked'; - if (attrs[name]) continue; + if (name in attrs) continue; + // We're checking prior entries aswell because "name in attrs" is not enough as the attributes may have different casing + for (let j = 0; j < names.length; j++) { + if (names[j].toLowerCase() === name) continue outer; + } } } diff --git a/packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html new file mode 100644 index 0000000000..87635c9545 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte b/packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte new file mode 100644 index 0000000000..e1ad517850 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte @@ -0,0 +1,10 @@ + + + + + + + +