fix: transform input defaults from spread
#16481
Closing issue
Describe the bug
take a component that includes an input with a defaultvalue (set via spreading). if you ssr the component, it fails to properly stringify this.
Reproduction
svelte repl link (go to js output and enable server): https://svelte.dev/playground/hello-world?version=5.36.13#H4sIAAAAAAAAA22NwQrCMBBEf2XZk4LUe6gFb36BF-MhNlsIrGloNlYJ-XcTKJ48zmPeTEZvnoQKL8Q8wzovbGFH1gnZPR5wckwR1S2jfELrNVD5Zp1D6OKLWBp7mEj_-Dh7IS91Bvs4Li7IoL2WiqOANWLgBBksTSaxXA0nUqBxyxqhaN8ff2LvfEgCueu65hY4DvVD6C2oZElU7jUZx6vzFtVkOFL5Am8TIenmAAAA
the issue can be distilled down to
import * as $ from 'svelte/internal/server'; console.log($.spread_attributes({ defaultValue: 'default' }, null))
returning defaultvalue="default" instead of value="default"
Logs
System Info
System:
OS: Linux 6.14 Ubuntu 25.04 25.04 (Plucky Puffin)
CPU: (12) arm64 unknown
Memory: 8.75 GB / 14.20 GB
Container: Yes
Shell: 4.0.1 - /usr/bin/fish
Binaries:
Node: 24.4.0 - ~/.local/share/pnpm/node
npm: 11.4.2 - ~/.local/share/pnpm/npm
pnpm: 10.11.0 - ~/.local/share/pnpm/pnpm
Severity
annoyance
Pull request
Fixes #16479.
The default props: defaultValue, defaultChecked for inputs were previously not renamed for SSR when set from an object spread, leading to unexpected results.
Before submitting the PR, please make sure you do the following
- 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
- Prefix your PR title with
feat:,fix:,chore:, ordocs:. - This message body should clearly illustrate what problems it solves.
- Ideally, include a test that fails without this PR but passes with it.
- If this PR changes code within
packages/svelte/src, add a changeset (npx changeset).
Tests and linting
- Run the tests with
pnpm testand lint the project withpnpm lint
Info
🦋 Changeset detected
Latest commit: fbfbbb3
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 1 package
| Name | Type |
|---|---|
| svelte | Patch |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
pnpm add https://pkg.pr.new/svelte@16481I think value act a bit differently than what defaultValue does on the client but I'll have to check...however this doesn't fix the reported bug. defaultValue and defaultChecked: even without spreading props defaultValue and defaultChecked doesn't get SSRd (which also make me think that it was a conscious decision to exclude them in the first place...so maybe the fix is to exclude them instead of converting them).
Also this should also be applied to textarea no? Thanks for taking a stab at this.
The defaultValue not getting SSR'd seems like an issue as well, since the SvelteKit docs recommend writing forms with support for no JavaScript. Since the value isn't SSR'd into the HTML template, users without JavaScript will have to fill out the entire form, which is especially annoying for update forms.
The
defaultValuenot getting SSR'd seems like an issue as well, since the SvelteKit docs recommend writing forms with support for no JavaScript. Since the value isn't SSR'd into the HTML template, users without JavaScript will have to fill out the entire form, which is especially annoying for update forms.
I agree...but you can also do that with a value directly...I don't think this is an unnecessary fix, was just pointing out that converting them to value and checked might not yield the desired result (I think there's some slight change in behaviour when you do so)
Unfortunately, value doesn't work if you use bind:value.
Also this should also be applied to
textareano?
I don't believe so - textarea doesn't support a value attribute in its markup.
According to specs we have:
prop defaultValue <-> attr value
prop value <-> input's current value with initial value of attr value
So, defaultValue should SSR into the value attribute. But into what should this SSR?:
<input value="foo" defaultValue="bar">
because they both set the same attribute value. I think value should take precedence in this case for better UX in no-JS case.
<input value="foo" defaultValue="bar">
I would expect this to SSR into <input value="bar"> and the value to be set to "foo" in JS.
But it can be done only if JS is enabled. Plus, somebody can do
<input bind:value defaultValue="">
to allow resetting a pre-filled form into an empty one.
I've pushed the change, which will now ignore defaultValue/defaultChecked if the non-default equivalent is set.
It seems that this doesn't entirely work - the hydration emits remove_input_defaults, which removes the value/checked attributes. How should I pass in the information so as not to remove them?
But it is set back in the following attribute_effect, isn't it?
Only when the props update after the initial hydration.
For now, my last thought is to pass the spreaded objects to remove_input_defaults so it sets the attribute to the defaultValue if it is present or removes the attribute otherwise.
I'm not sure how to do that, I tried passing in the SpreadAttribute.expressions but that ends up calling functions ({...get_props()}) twice.
@daimond113 please check how it is now.
Seems to work as expected. Thank you!
thank you!