Remote `form` with `.for()` duplicate requests exponentially
Describe the bug
When I have multiple forms on the same page that submits to the same Remote function, Svelte recommends me to use a form.for(key) to distinguish between difference form instances (Don't seem to see docs for this anywhere, but the message is in console.log).
Uncaught error: A form object can only be attached to a single `<form>` element.
To create multiple instances, use `updateScore.for(key)
However, using .for() duplicates my request exponentially. First submit sends 1 requests, 2nd submit sends 2 requests, 3rd submits send 4 requests, 4th submit sends 8 requests, and so on...
What strange is that if I remove the .for() part, the Remote form looks to work like normal again.
Reproduction
Here is a Github reproduction repo: https://github.com/bhuynhdev/sveltekit-issue-14546 Here is a SvelteLab link with same content: https://www.sveltelab.dev/gpq6890y483k245?files=.%2Fsrc%2Froutes%2F%2Bpage.svelte
The reproduction step is simple:
- Create new project with
npx sv create my-app - Install
valibot:npm install valibot - Change
svelte.config.jsto include experimental features
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
compilerOptions: {
experimental: {
async: true
},
},
kit: {
adapter: adapter(),
experimental: {
remoteFunctions: true
},
},
};
export default config;
- Create
routes/data.remote.jswith following content:
import * as v from 'valibot'
import { form, query } from '$app/server'
let SCORE = {
"amy": 1,
// "bob": 2,
};
export const listScore = query(async () => {
const dataAsPromise = await new Promise(resolve => resolve(Object.entries(SCORE)))
return dataAsPromise
})
export const updateScore = form(
v.object({name: v.picklist(Object.keys(SCORE))}),
async (form) => {
SCORE[form.name] = Number(SCORE[form.name]) + 1
await listScore().refresh()
}
)
- Update
page.svelteto following content:
<script>
import { listScore, updateScore } from "./data.remote";
const score = listScore();
</script>
<div>
<pre>{JSON.stringify(score.current, null, 2)}</pre>
{#each score.current || [] as [name, _], i (name)}
<!-- enhance to prevent form reset and potentially do other thing like show toast -->
<form
{...updateScore.for(name).enhance(async ({ submit }) => await submit())}
>
<input type="hidden" name="name" value={name} />
<button type="submit">Submit {i}</button>
</form>
{/each}
</div>
- Run the page with
npm run dev - Try clicking the Submit button multiple time on
http://localhost:5173
Logs
System Info
System:
OS: Linux 6.6 Ubuntu 24.04.2 LTS 24.04.2 LTS (Noble Numbat)
CPU: (32) x64 13th Gen Intel(R) Core(TM) i9-13900HX
Memory: 13.50 GB / 15.46 GB
Container: Yes
Shell: 5.2.21 - /bin/bash
Binaries:
Node: 22.17.0 - ~/.local/share/mise/installs/node/22.17.0/bin/node
npm: 10.9.2 - ~/.local/share/mise/installs/node/22.17.0/bin/npm
pnpm: 10.15.1 - ~/.local/share/pnpm/pnpm
npmPackages:
@sveltejs/adapter-auto: ^6.0.0 => 6.1.0
@sveltejs/kit: ^2.22.0 => 2.43.5
@sveltejs/vite-plugin-svelte: ^6.0.0 => 6.2.1
svelte: ^5.0.0 => 5.39.6
vite: ^7.0.4 => 7.1.7
Severity
blocking an upgrade
Additional Information
No response
I think the problem is a combination of:
-
{#each}rerunning wheneverscore.currentrefreshes creating multiple forms in cache with the same.for()name -
.enhance()programmatically submitting all cached forms with the designated.for()name
By removing the .enhance() it behaves as expected.
https://www.sveltelab.dev/44u25vu267qp2d7
Alternatively you should prevent the loop from rerunning by importing the names in a separate query, or if hard coded, as a separate import.
https://www.sveltelab.dev/y795sdtwg5wiki0
Also, this appears to have been previously reported: https://github.com/sveltejs/kit/issues/14498
Ultimately when the each loop, or some other effect, reruns it should clean up the existing form(s) from cache to prevent extra submissions.
Just copying my reproduction from the duplicate issue above into this one in case it helps. The circumstances required to trigger the issue seemed very particular in my case.
Just wish I had found this issue before spending hours narrowing down the problem 😅
Reproduction: https://www.sveltelab.dev/j093ob9ce3xk50c?files=.%2Fsrc%2Froutes%2F%2Bpage.svelte%2C.%2Fsrc%2Froutes%2Fapi.remote.ts
Click the button and observe that the calls made double each time. Both the client enhance function and the server form function log when they are called.
The circumstances I've narrowed it down to so far are
- Multiple instances of a form using .for() inside of an async each block calling a remote query
- A top level await followed by a derived promise, which is then awaited in the template
- The forms use .enhance()
I can reproduce locally in my application with only an enhance() on the form. No need to use the for().
I don't think it's related to kit, but more to svelte itself.
I've never noticed it with version 5.41.x Testing with 5.45.2 yesterday, and noticed it right away. Removing the enhance makes it back to normal instantly.
@bcharbonnier Did that ever resolve for you or did you just move away from using enhance?
Ran into this today with just enhance on a single form, meaning no .for, but it was not happening consistently. Closed the tab and reopened app in another, everything was fine for 10-15 minutes and then it starts popping up again. I feel like I've been seeing a lot of weird little ghosts like that since switching over to async svelte & remote queries/forms.
Update: Also noticed that, when this issue was happening, prelight-only validation also wasn't working - it was back to making a request to the server for validation that otherwise worked client-side.