Skip to content

Select Elements Don't Update When Value Changes #14751

Closed

Description

@abdelfattahradwan

Describe the bug

I have a query which loads some data that is then used as defaults for a form.

import { form, query } from '$app/server';
import { type } from 'arktype';

export const getFormDefaults = query(() => ({
	colour: 'green',
	opacity: 0.5
}));

const TestFormSchema = type({
	colour: '"red" | "green" | "blue"',
	opacity: 'number'
});

export const testForm = form(TestFormSchema, async (data) => {
	console.log('Your favourite colour is:', data.colour);
	console.log('Your chosen opacity is:', data.opacity);
});

When I have a select element, whenever I attempt to change the selected value, it updates the value for the form fields, but the select element itself does not update the displayed value.

<script lang="ts">
	import { getFormDefaults, testForm } from '$lib/test.remote';

	const formDefaults = $derived(await getFormDefaults());
</script>

<h1>Test</h1>

<main>
	<form {...testForm}>
		<label for="favourite-colour-selector">Favourite Colour</label>
		<select
			id="favourite-colour-selector"
			{...testForm.fields.colour.as('select')}
			value={formDefaults.colour}
		>
			<option value="red">Red</option>
			<option value="green">Green</option>
			<option value="blue">Blue</option>
		</select>
		<label for="opacity-input">Opacity</label>
		<input
			id="opacity-input"
			{...testForm.fields.opacity.as('number')}
			defaultValue={formDefaults.opacity}
			value={formDefaults.opacity}
		/>
		<p>Your favourite colour is {testForm.fields.colour.value() ?? 'N/A'}</p>
		<p>Your chosen opacity is {testForm.fields.opacity.value() ?? 'N/A'}</p>
		<button type="submit">Submit</button>
	</form>
</main>

Reproduction

  1. Clone https://github.com/abdelfattahradwan/sveltekit-select-bug
  2. Run the dev script. (e.g. npm run dev)
  3. Open localhost:5173
  4. Try changing the value of the "Favourite Colour".

Logs

N/A

System Info

System:
    OS: Windows 11 10.0.26200
    CPU: (28) x64 Intel(R) Core(TM) i7-14700K
    Memory: 35.58 GB / 63.70 GB
  Binaries:
    Node: 22.20.0 - C:\Program Files\nodejs\node.EXE
    npm: 11.6.1 - C:\Users\Abdo\AppData\Roaming\npm\npm.CMD
    bun: 1.3.0 - C:\Users\Abdo\.bun\bin\bun.EXE
    Deno: 2.5.4 - C:\Program Files\deno\deno.EXE
  Browsers:
    Chrome: 141.0.7390.108
    Edge: Chromium (140.0.3485.81)
    Firefox: 144.0 - C:\Program Files\Mozilla Firefox\firefox.exe
    Internet Explorer: 11.0.26100.1882
  npmPackages:
    @sveltejs/adapter-auto: ^7.0.0 => 7.0.0 
    @sveltejs/kit: ^2.47.1 => 2.47.1 
    @sveltejs/vite-plugin-svelte: ^6.2.1 => 6.2.1 
    svelte: ^5.40.2 => 5.40.2 
    vite: ^7.1.10 => 7.1.10

Severity

annoyance

Additional Information

No response

Activity

  1. sillvva commented on Oct 17, 2025

    @sillvva
    Contributor

    You should set default values in the script block with form.fields.set({ ... }). Assigning values like that overrides the remote form field behavior.

    Docs: https://svelte.dev/docs/kit/remote-functions#form-Getting-setting-inputs

    Example: https://stackblitz.com/edit/svetekit-template-nxhl9tqut?file=src%2Froutes%2F%2Bpage.svelte,src%2Froutes%2Ftest.remote.ts

  2. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    You should set default values in the script block with form.fields.set({ ... }). Assigning values like that overrides the remote form field behavior.

    Docs: https://svelte.dev/docs/kit/remote-functions#form-Getting-setting-inputs

    Example: https://stackblitz.com/edit/svetekit-template-nxhl9tqut?file=src%2Froutes%2F%2Bpage.svelte,src%2Froutes%2Ftest.remote.ts

    The example I included above (and in the reproduction repository) specifically has a remote query because it is intended to mimic an "edit existing entity" form.

    If I do this:

    <script lang="ts">
    	import { getFormDefaults, testForm } from '$lib/test.remote';
    
    	const formDefaults = $derived(await getFormDefaults());
    
    	testForm.fields.set({
    		colour: formDefaults.colour as 'red' | 'green' | 'blue',
    		opacity: formDefaults.opacity
    	});
    </script>

    I get a warning saying This reference only captures the initial value of `formDefaults`. Did you mean to reference it inside a closure instead? https://svelte.dev/e/state_referenced_locallysvelte(state_referenced_locally)

  3. sillvva commented on Oct 17, 2025

    @sillvva
    Contributor

    Unless you're intending to refresh the default values, you don't need the $derived().

  4. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    Unless you're intending to refresh the default values, you don't need the $derived().

    I do intend to refresh them. After editing the form values and submitting, the new values are supposed to override the defaults.

    For example, I am on an "Edit Post" page, and I use a remote query to get the current post details. When I change, say, the post title and submit, the form refreshes the remote query value, and the new title should show up instead of the current one.

  5. sillvva commented on Oct 17, 2025

    @sillvva
    Contributor

    Here's an example setting initial values and then updating them when the query refreshes:
    https://stackblitz.com/edit/svetekit-template-nxhl9tqut?file=src%2Froutes%2F%2Bpage.svelte,src%2Froutes%2Ftest.remote.ts

  6. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    Here's an example setting initial values and then updating them when the query refreshes: https://stackblitz.com/edit/svetekit-template-nxhl9tqut?file=src%2Froutes%2F%2Bpage.svelte,src%2Froutes%2Ftest.remote.ts

    Thanks for the example 🙏🏻 I used an $effect initially as well, but since $effects aren't run during SSR, I don't think they will work with JS disabled. I have to support no-JS users in this particular case.

  7. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    Also, I wonder why the number input works just fine, while the select doesn't? It seems odd to me.

  8. sillvva commented on Oct 17, 2025

    @sillvva
    Contributor

    Honestly, I'm not sure what the difference is caused by.

    If your users don't have JS, then $derived() shouldn't matter in that case either. I only support JS-enabled environments in my app, so I've not tested this use case. But I believe in a no JS environment, the form would act like a normal HTML form and reload the page when submitted. Then the initial .set() should rerun at that time.

  9. sillvva commented on Oct 17, 2025

    @sillvva
    Contributor

    As a workaround, try this:

    		<select
    			id="favourite-colour-selector"
    			{...testForm.fields.colour.as('select')}
    			bind:value={
    				() => testForm.fields.colour.value() ?? formDefaults.colour
    				(val) => testForm.fields.colour.set(val)
    			}
    		>
    			<option value="red">Red</option>
    			<option value="green">Green</option>
    			<option value="blue">Blue</option>
    		</select>
  10. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    If your users don't have JS, then $derived() shouldn't matter in that case either. I only support JS-enabled environments in my app, so I've not tested this use case. But I believe in a no JS environment, the form would act like a normal HTML form and reload the page when submitted. Then the initial .set() should rerun at that time.

    When a no-JS users visit the app, they get an SSR'd page with everything rendered correctly. When they submit the form, since there is no JS, the page is basically reloaded, and the form gets re-rendered correctly with the updated values.

    The issue actually impacts JS users. Without the $derived, when I submit the form, nothing refreshes, and the old values remain. Also, being unable to see the changes made to the select is, well, bad for the UX to say the least 😅

  11. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    As a workaround, try this:

      <select
      	id="favourite-colour-selector"
      	{...testForm.fields.colour.as('select')}
      	bind:value={
      		() => testForm.fields.colour.value() ?? formDefaults.colour
      		(val) => testForm.fields.colour.set(val)
      	}
      >
      	<option value="red">Red</option>
      	<option value="green">Green</option>
      	<option value="blue">Blue</option>
      </select>
    

    Oh, I completely forgot about bind getters/setters! That works!

  12. sillvva commented on Oct 17, 2025

    @sillvva
    Contributor

    If your users don't have JS, then $derived() shouldn't matter in that case either. I only support JS-enabled environments in my app, so I've not tested this use case. But I believe in a no JS environment, the form would act like a normal HTML form and reload the page when submitted. Then the initial .set() should rerun at that time.

    When a no-JS users visit the app, they get an SSR'd page with everything rendered correctly. When they submit the form, since there is no JS, the page is basically reloaded, and the form gets re-rendered correctly with the updated values.

    The issue actually impacts JS users. Without the $derived, when I submit the form, nothing refreshes, and the old values remain. Also, being unable to see the changes made to the select is, well, bad for the UX to say the least 😅

    My second Stackblitz example should actually work for both cases as the $effect would only apply to JS users. Non-JS users would still have the page reload. Both users should see the updated values.

  13. abdelfattahradwan commented on Oct 17, 2025

    @abdelfattahradwan
    Author

    My second Stackblitz example should actually work for both cases as the $effect would only apply to JS users. Non-JS users would still have the page reload. Both users should see the updated values.

    Now that I think about it, it would work, yeah.

    I will stick with the custom getter/setter, though. I try to avoid $effect as much as possible for simple things like this.

    Thank you so much for the help 🙏🏼

  14. sillvva commented on Oct 18, 2025

    @sillvva
    Contributor

    Just as an aside, here's a version that doesn't use $effect(). I feel like this is a lot simpler.

    https://stackblitz.com/edit/svetekit-template-nxhl9tqut-dnw5pnez?file=src%2Froutes%2F%2Bpage.svelte

  15. added
    formsStuff relating to forms and form actions
    on Feb 25, 2026
  16. abdelfattahradwan commented on Jun 18, 2026

    @abdelfattahradwan
    Author

    I haven't been able to reproduce this particular issue for quite some time now. select elements' values seem to update correctly when I make a selection now. I assume that means the issue has been fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    formsStuff relating to forms and form actions

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions