sveltejs / sveltejs/kit

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

Open
#17,131 0 comments 1 reaction 0 assignees View on GitHub

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

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

  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

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.