svelte
svelte copied to clipboard
Hydration clobbers input into form
If your form has just been server rendered and you initial bind the values to empty string, when a user starts to enter data into the form it will get clobbered by hydration.
You can disable the inputs until the page hydrates, but I like my site(s) to be snappy and don't want to block the form out until hydration, admittedly its not an eternity, but its enough for me to drag it to the table.
RH has indicated that if this were standard behaviour some use cases would present weird / unsuitable UX experience, for example typing into a search input doesn't result in the autocomplete dropdown appearing.
In which case I would like to request some optin behaviour to prevent hydration from clobbering the form inputs.
<input bind:value:nohydrate=username>
or something of that ilk?
To be fair, this is caused by an excessively large file, but where is that cutoff and what will be the demands of user apps?
Closing as a duplicate of https://github.com/sveltejs/svelte/issues/4308. This issue isn't specific to form input, but multiple elements that are negatively affected by being destroyed and recreated such as videos and animations
I feel that it might make sense to keep this as an separate open issue. I don't believe the cause is only destroying/recreating nodes but also calling set_input_value
when mounting the elements. So even if we never destroy & recreate, form inputs will likely reset.
Ticket #4308 is solved but the problem persists, where hydration removes the input that has been done up to this point.
You can disable the inputs until the page hydrates,
@ispyinternet do you have a nice way of doing this? Or just some boolean variable that gets changed on "onMount"?
@benmccann pinging you directly as you were the one closing this ticket. I still the problem described in this ticket still exists. Could you revive this ticket? :)
Cheers
@andreasnuesslein could you provide a minimal reproduction? Assuming the original case was addressed we will not have a way to reproduce otherwise
@andreasnuesslein could you provide a minimal reproduction? Assuming the original case was addressed we will not have a way to reproduce otherwise
I still mean to create an example. I've only been out of business with the flu for the last 10 days :\ Slowly gettin' better.
I finally made an example @benmccann
https://github.com/andreasnuesslein/svelte-ssr-issue
Basically I created a new sveltekit
project (npm init svelte svelte-ssr-reset-example
) and modified the index.svelte
and created the sleep.svelte
. No other changes. The README.md contains the steps to reproduce.
Second this, prepared a nearly identical repro: https://github.com/thoughtspile/svelte-hydration-repro
Reproduced for me with latest svelte. Also it resets focus on input element too during hydration.
One way or another @benmccann , I understand it's not on the top of the list at the moment, but could you please re-open this issue? It's just not solved yet.
Cheers
This has been very frustrating experience for me as well, and should certainly be reopened. However, I'd like to share a workaround in the meantime.
During initialization, it is possible to set the bound variable to the browsers current element's value. Like so:
<script>
let value
if (document) {
value = document.querySelector('input[name="x"]').value
}
</script>
<input name="x" bind:value />
N.B.: I'm using this with bind:group
in a sveltekit app and guard it with browser
from $app/environment
In https://github.com/sveltejs/svelte/issues/8266#issuecomment-1456411585 there was a suggestion for claiming <input>
nodes during hydration that seems good to me; copying it here so that it won't get lost if #8266 is closed as a duplicate.
input = claim_element(nodes, "INPUT", {});
// When input is different from the context value
if (input.value !== /*a*/ ctx[0]) {
// We use the binding handler to update it's value
/*input_input_handler*/ ctx[1].call(input).
}
We run this userland workaround in production, seems to do the trick with the caveat of not triggeing on:input
:
<script context="module">
const preHydrationValues = new WeakMap();
// client-side only
typeof document !== 'undefined' &&
document.querySelectorAll('input').forEach((input) => {
// only for changed inputs
if (input.value !== input.getAttribute('value')) {
preHydrationValues.set(input, input.value);
}
});
</script>
<script>
export let value;
function restorePreHydrationValue(node) {
const savedValue = preHydrationValues.get(node);
if (savedValue) {
node.value = savedValue;
value = savedValue;
preHydrationValues.delete(node);
}
};
</script>
<input
use:restorePreHydrationValue
bind:value
{...$$restProps}
/>
I also found this bug very frustrating and hard to diagnose. Seems like a huge gotcha that needs to be pointed out in the docs when using universal loading. My solution has to just been to not use universal loading at all and instead use server loading, but I am curious to know what other people building Sveltekit apps are doing and if this is viewed as a bug that plans to get fixed or what the recommended way of dealing with this is.
I think this will probably be addressed by Svelte 5, but am not positive. We should test when it's available
I think this will probably be addressed by Svelte 5, but am not positive. We should test when it's available
@benmccann That would be great, I'll test it out when it comes out
yes, it's a supper annoying issue, basically if user touch input before hydration is finished - it should be avoided by hydration. It still race condition possible but it should significantly mitigate it.
:partying_face: