Untouched remote form `<select>` renders blank when `validate()` runs on `onchange`
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 20.8k
- Forks
- 2.3k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 156
Description
Describe the bug
An untouched <select>, bound with a remote form field's .as('select'), is left displaying a blank value when preflight validation runs from an onchange handler on an adjacent field.
The <select>'s first option is <option value="">-- pick --</option>, and it is never touched, so it displays -- pick --. Editing an adjacent <input> and blurring it fires onchange → validate(), and afterwards the <select> displays blank.
That blank display matches none of its options — no option has a blank text label — so the control ends up showing a value it can never be set to. In the DOM, its selectedIndex is -1 (no option selected), even though the first option's value ("") does match the field's value. The <select> was also never dirtied, so it shouldn't have been part of the validation.
- Expected: validating after editing an adjacent field leaves the untouched
<select>showing-- pick --(selectedIndex 0). - Actual: the untouched
<select>displays blank (selectedIndex -1).
cc @hyunbinseo
https://github.com/user-attachments/assets/d611a874-7185-4ca1-b7d3-2b7356494f19
Reproduction
schema.ts
import { object, optional, string } from 'valibot';
export const MreSchema = object({
picked: optional(string(), ''),
text: optional(string(), ''),
});
data.remote.ts
import { form } from '$app/server';
import { MreSchema } from './schema.ts';
export const mreForm = form(MreSchema, async () => {
return { ok: true };
});
+page.svelte
<script lang="ts">
import { mreForm as _mreForm } from './data.remote.ts';
import { MreSchema } from './schema.ts';
const uid = $props.id();
const mreForm = _mreForm.for(uid);
</script>
<form {...mreForm.preflight(MreSchema)} onchange={() => mreForm.validate()}>
<select {...mreForm.fields.picked.as('select')}>
<option value="">-- pick --</option>
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<input {...mreForm.fields.text.as('text')} placeholder="type here, then blur" />
<button>submit</button>
</form>
Steps (do not touch the <select>):
- The
<select>shows-- pick --. - Type into the text input and blur it — this fires
onchange→validate(). - The untouched
<select>now displays blank (itsselectedIndexis-1) instead of-- pick --.
Removing onchange={() => mreForm.validate()} leaves the <select> alone.
Logs
System Info
System:
OS: Linux 7.2 Fedora Linux 44 (Forty Four)
CPU: (8) x64 Intel(R) Core(TM) Ultra 7 258V
Memory: 9.47 GB / 30.86 GB
Container: Yes
Shell: 5.3.9 - /bin/bash
Binaries:
Node: 26.8.2 - /home/cohe/prj/all-learners/lms/node_modules/.bin/node
npm: 11.16.0 - /usr/bin/npm
pnpm: 12.4.1 - /home/cohe/.local/share/pnpm/bin/pnpm
Browsers:
Chrome: 153.0.8010.47
Firefox: 155.0
Firefox Developer Edition: 155.0
npmPackages:
@sveltejs/adapter-node: 5.5.6 => 5.5.6
@sveltejs/kit: ^2.70.3 => 2.70.3
@sveltejs/vite-plugin-svelte: ^7.3.0 => 7.3.0
svelte: ^5.57.0 => 5.57.0
vite: ^8.3.0 => 8.3.0
Severity
annoyance
Additional Information
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue using schema.ts, data.remote.ts, and +page.svelte, then trace the remote form field handling reached by mreForm.validate() and the select binding. Confirm the untouched select retains selectedIndex 0 and displays -- pick -- after the adjacent input changes and blurs, while the existing reproduction still validates.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 70/100