#17131·kit

Untouched remote form `<select>` renders blank when `validate()` runs on `onchange`

Author: CheHyeonYeongCreated Sep 18, 2026Updated Sep 18, 2026

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 onchangevalidate(), 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

typescript
import { object, optional, string } from 'valibot';

export const MreSchema = object({
	picked: optional(string(), ''),
	text: optional(string(), ''),
});

data.remote.ts

typescript
import { form } from '$app/server';
import { MreSchema } from './schema.ts';

export const mreForm = form(MreSchema, async () => {
	return { ok: true };
});

+page.svelte

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>):

  1. The <select> shows -- pick --.
  2. Type into the text input and blur it — this fires onchangevalidate().
  3. The untouched <select> now displays blank (its selectedIndex is -1) instead of -- pick --.

Removing onchange={() => mreForm.validate()} leaves the <select> alone.

Logs

bash

System Info

bash
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