Skip to content

form remote functions with reactive initial values #15500

@DuCanhGH

Description

@DuCanhGH

Describe the bug

Suppose I have a form which has initial values dependent on some sort of reactive data. It can be $state, $derived, etc. I need to have that data in the initial value of some input fields. I want all the fields to be loaded during SSR so that users don't see unloaded fields before hydration, so $effect seems to be out of the question.

This is what I initially tried:

<script lang="ts">
  import { page } from "$app/state";
  import { resolve } from "$app/paths";
  import { loadId, sendId } from "../../data.remote";

  const idPrefix = $derived(page.params.idPrefix ?? "default");
  const id = $derived(await loadId({ idPrefix }));

  const form = $derived(sendId.for(id.received));

  form.fields.set({ text: `Default text for ${id.received}` });
</script>
<form {...form}>
  <input
    type="text"
    placeholder="Enter a value to send to the server"
    {...form.fields.text.as("text")}
  />
  <button type="submit">Submit</button>
</form>

This doesn't work, as I'm referencing form and id outside of a closure. If I were to do a client navigation to the same page but different params, I'd get stale input fields.

I then tried to put the form.fields.set call in form's $derived:

const form = $derived.by(() => {
  const form = sendId.for(id.received);
  form.fields.set({ text: `Default text for ${id.received}` });
  return form;
});

This doesn't work either, as form.fields.set is itself a state mutation, triggering this error when e.g. submitting the form:

Uncaught Svelte error: state_unsafe_mutation
Updating state inside `$derived(...)`, `$inspect(...)` or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
https://svelte.dev/e/state_unsafe_mutation

Currently I'm using a workaround like so:

const form = $derived.by(() => {
  const text = `Default text for ${id.received}`;
  const form = sendId.for(id.received);
  untrack(() => form.fields.set({ text }));
  return form;
});

This works, but all the initial values have to be calculated outside of untrack, which isn't quite neat.

I supposed that the form remote function might be lacking some sort of initial value mechanism, which is why I filed an issue. What I have in mind would be a bit similar to .for, such as .values({ text: `Default text for ${id.received}` }).

Another solution I have in mind would be to make .as accept an additional defaultValue argument like so: form.fields.text.as("text", `Default text for ${id.received}`). This feels more ergonomic in my opinion; however, since the fields seem to be controlled by states, I don't think it's as viable as the aforementioned.

Thank you!

Reproduction

https://github.com/DuCanhGH/sv-reprod-remote-reactive

Logs

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

Uncaught Svelte error: state_unsafe_mutation
Updating state inside `$derived(...)`, `$inspect(...)` or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
https://svelte.dev/e/state_unsafe_mutation

System Info

System:
    OS: Linux 6.18 cpe:/o:nixos:nixos:26.05 26.05 (Yarara)
    CPU: (16) x64 AMD Ryzen 7 9800X3D 8-Core Processor
    Memory: 14.85 GB / 30.51 GB
    Container: Yes
    Shell: 4.5.0 - /run/current-system/sw/bin/fish
  Binaries:
    Node: 22.22.0 - /run/current-system/sw/bin/node
    Yarn: 1.22.22 - /run/current-system/sw/bin/yarn
    npm: 10.9.4 - /run/current-system/sw/bin/npm
    pnpm: 10.29.1 - /run/current-system/sw/bin/pnpm
  Browsers:
    Chromium: 145.0.7632.116
    Firefox: 148.0
    Firefox Developer Edition: 148.0
  npmPackages:
    @sveltejs/adapter-auto: ^7.0.0 => 7.0.1 
    @sveltejs/kit: ^2.50.2 => 2.53.4 
    @sveltejs/vite-plugin-svelte: ^6.2.4 => 6.2.4 
    svelte: ^5.51.0 => 5.53.7 
    vite: ^7.3.1 => 7.3.1

Severity

annoyance

Additional Information

No response

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