Skip to content

form remote functions with reactive initial values #15500

Closed

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

Activity

  1. added
    formsStuff relating to forms and form actions
    on Mar 11, 2026
  2. einarpersson commented on Jun 3, 2026

    @einarpersson

    I think it would be nice if it was possible to set the initial value on the form in the markup close to where you'd put enhance, something like

    <form ...editUser.set(user).enhance(... but now .set lives on the .fields property so I don't know.. But yeah, it would be nice to be able to do this without any script tag

  3. mgrubinger commented on Jun 3, 2026

    @mgrubinger
    Contributor

    This should work if you use the value as the second argument to text.as('text', YOUR-VALUE-HERE) in the latest SvelteKit.
    See https://svelte.dev/docs/kit/remote-functions#form-Fields:~:text=For%20most%20input%20types%2C%20you%20can%20also%20pass%20a%20second%20argument%20%E2%80%94%20.as(type%2C%20value)%20%E2%80%94%20to%20control%20the%20rendered%20value%3A and the PR #15577

    I quickly tested it with your linked repo (with @sveltejs/kit@2.62.0 + svelte@5.56.1) and it seems to work if you just remove the .set(..) calls and instead spread it like this onto the input
    {...form.fields.text.as("text", Default text for ${id.received})}

    (However, it's possible I'm missing something from your usecase)

  4. DuCanhGH commented on Jun 3, 2026

    @DuCanhGH
    Author

    @mgrubinger nice, thanks! I didn't notice the fix because my issue was not linked in the PR. Closing this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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