From 378bb25097088c2277aa063408c62818cc1f6c4e Mon Sep 17 00:00:00 2001 From: jdoughty04 Date: Sun, 31 May 2026 12:16:37 -0400 Subject: [PATCH] fix: set input type before spread value (#18345) ### Before submitting the PR, please make sure you do the following - [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs - [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`. - [x] This message body should clearly illustrate what problems it solves. - [x] Ideally, include a test that fails without this PR but passes with it. - [x] If this PR changes code within `packages/svelte/src`, add a changeset (`npx changeset`). ### Tests and linting - [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` Fixes #18332. When spread props put `value` before a later `type="hidden"` on an ``, `set_attributes` currently writes the value while the element is still a text input. Browsers sanitize text input values by removing newlines, so the hidden input permanently loses them before `type` is applied. This sets an input's `type` first whenever the same spread update also includes `value` or `__value`, so the existing value handling runs with the final input type. The new runtime-browser sample covers both the problematic spread-first order and the already-working type-first order. Validation: - `corepack pnpm lint` - `CI=1 corepack pnpm test` - `corepack pnpm vitest run packages/svelte/tests/runtime-browser/test.ts -t input-type-before-value-spread` --------- Co-authored-by: Rich Harris Co-authored-by: Rich Harris --- .changeset/input-type-before-value.md | 5 +++++ .../internal/client/dom/elements/attributes.js | 9 +++++++++ .../input-type-before-value-spread/_config.js | 15 +++++++++++++++ .../input-type-before-value-spread/main.svelte | 7 +++++++ 4 files changed, 36 insertions(+) create mode 100644 .changeset/input-type-before-value.md create mode 100644 packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/main.svelte diff --git a/.changeset/input-type-before-value.md b/.changeset/input-type-before-value.md new file mode 100644 index 0000000000..68d02f920f --- /dev/null +++ b/.changeset/input-type-before-value.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve newlines in spread input values when the `type` attribute is applied after `value` diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index a193a70dd5..589685b5f2 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -332,6 +332,15 @@ function set_attributes( var setters = get_setters(element); + if (element.nodeName === INPUT_TAG && 'type' in next && ('value' in next || '__value' in next)) { + var type = next.type; + + if (type !== current.type || (type === undefined && element.hasAttribute('type'))) { + current.type = type; + set_attribute(element, 'type', type, skip_warning); + } + } + // since key is captured we use const for (const key in next) { // let instead of var because referenced in a closure diff --git a/packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/_config.js new file mode 100644 index 0000000000..7aeaf38eb0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/_config.js @@ -0,0 +1,15 @@ +import { test } from '../../test'; + +const value = 'line1\nline2\nline3'; + +export default test({ + mode: ['client'], + test({ assert, target }) { + const [spread_first, type_first] = target.querySelectorAll('input'); + + assert.equal(spread_first?.type, 'hidden'); + assert.equal(spread_first?.value, value); + assert.equal(type_first?.type, 'hidden'); + assert.equal(type_first?.value, value); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/main.svelte new file mode 100644 index 0000000000..16e72ab704 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/input-type-before-value-spread/main.svelte @@ -0,0 +1,7 @@ + + + +