Describe the bug
When spreading props onto an <input> element and explicitly setting type="hidden" after the spread, newlines in the value are silently stripped on initial client-side render.
Minimal case:
<script>
const spread = $state({ name: "field", value: "line1\nline2\nline3" });
</script>
<!-- BUG: newlines stripped -->
<input {...spread} type="hidden" />
<!-- OK: newlines preserved -->
<input type="hidden" {...spread} />
Root cause
set_attributes() in src/internal/client/dom/elements/attributes.js iterates the merged props object with for (const key in next). JavaScript iterates object keys in insertion order, so for:
{ ...spread(), type: "hidden" }
value (from the spread) is visited before type. At that point the element is still type="text" (the default). Setting .value on a text input triggers the HTML value sanitization algorithm, which strips \n and \r. By the time type is set to "hidden", the newlines are permanently lost.
The reverse order ({ type: "hidden", ...spread() }) works because type appears first in insertion order and is set before value.
Suggested fix
When set_attributes encounters both type and value for an <input> element, it should set type before value regardless of iteration order. This mirrors what React does — React's DOM reconciler explicitly sets type before other properties on input elements to avoid this class of bugs.
A minimal fix could be:
// In set_attributes(), before the main loop:
if (element.nodeName === 'INPUT' && 'type' in next) {
const type = next.type;
if (type !== element.type) {
element.type = type;
}
}
Related
This bug directly affects the workaround mentioned in sveltejs/kit#15887, where <input {...form.fields.id.as("text")} type="hidden" /> is suggested as an alternative to as("hidden", value). That pattern relies on type="hidden" overriding the spread, but due to this bug the attribute ordering causes silent data corruption — newlines in the value are stripped before type is applied. Until this is fixed, the only safe ordering is <input type="hidden" {...spread} />.
Impact
This affects any pattern where hidden form inputs are created with spread props — a common pattern in form libraries. It's particularly insidious because:
- It only affects client-side rendering (SSR hydration preserves newlines since the server renders correct HTML)
- It's completely silent — no warning, no error
- It causes data corruption (newlines permanently lost on form submission)
Reproduction
https://svelte.dev/playground/fed81261ad3f49ebaf66f41a16914264?version=5.56.0
Logs
No response
System Info
Svelte 5.56.0 (and likely all Svelte 5 versions using set_attributes)
Severity
annoyance
Describe the bug
When spreading props onto an
<input>element and explicitly settingtype="hidden"after the spread, newlines in thevalueare silently stripped on initial client-side render.Minimal case:
Root cause
set_attributes()insrc/internal/client/dom/elements/attributes.jsiterates the merged props object withfor (const key in next). JavaScript iterates object keys in insertion order, so for:value(from the spread) is visited beforetype. At that point the element is stilltype="text"(the default). Setting.valueon a text input triggers the HTML value sanitization algorithm, which strips\nand\r. By the timetypeis set to"hidden", the newlines are permanently lost.The reverse order (
{ type: "hidden", ...spread() }) works becausetypeappears first in insertion order and is set beforevalue.Suggested fix
When
set_attributesencounters bothtypeandvaluefor an<input>element, it should settypebeforevalueregardless of iteration order. This mirrors what React does — React's DOM reconciler explicitly setstypebefore other properties on input elements to avoid this class of bugs.A minimal fix could be:
Related
This bug directly affects the workaround mentioned in sveltejs/kit#15887, where
<input {...form.fields.id.as("text")} type="hidden" />is suggested as an alternative toas("hidden", value). That pattern relies ontype="hidden"overriding the spread, but due to this bug the attribute ordering causes silent data corruption — newlines in the value are stripped beforetypeis applied. Until this is fixed, the only safe ordering is<input type="hidden" {...spread} />.Impact
This affects any pattern where hidden form inputs are created with spread props — a common pattern in form libraries. It's particularly insidious because:
Reproduction
https://svelte.dev/playground/fed81261ad3f49ebaf66f41a16914264?version=5.56.0
Logs
No response
System Info
Severity
annoyance