Skip to content

Conversation

@sillvva
Copy link
Contributor

@sillvva sillvva commented Oct 26, 2025

fixes #14802
fixes #14787

According to the docs, the result is supposed to be

... ephemeral — it will vanish if you resubmit, navigate away, or reload the page.

But that is not currently the case for navigating. The same is true of the values, issues, etc.

When you import a remote form into a page, you're importing an instance of the form. That instance is cached in memory so all properties tied to it are preserved when you navigate from page to page.

This PR changes the import into a factory function that creates a new instance each time the page/component loads, and removes the instance when it unloads.

Demo: https://stackblitz.com/edit/sveltekit-template-sjtkof-etscgbpf?file=package.json,src%2Froutes%2F%2Bpage.svelte

  <script lang="ts">
    import { todo } from "./form.remote.ts";
  </script>
  
- <form {...todo}> <!-- same instance every time -->
+ <form {...todo()}> <!-- creates a new instance -->
    ...
  </form>

And instead of .for(), you can now pass the key to the function.

<script lang="ts">
  import { todo, getTodos } from "./form.remote.ts";
  const todos = await getTodos();
</script>

{#each todos as todo (todo.id)}
  {@const form = todo(todo.id)}
  <form {...form}>
    ...
  </form>
{/each}

This also opens up the possibility of making the form configurable like this:

const data = $state(initialData);

const scopedTodo = $derived(todo({
  key: 'scoped', // key, for, or id
  preflight: schema,
  initialData: data, // $state
  /** Show errors on load */
  prevalidate: true,
  /** Clear values on submit, for non-enhanced forms */
  clearOnSubmit: false
}));

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

Edits

  • Please ensure that 'Allow edits from maintainers' is checked. PRs without this option may be closed.

@changeset-bot
Copy link

changeset-bot bot commented Oct 26, 2025

🦋 Changeset detected

Latest commit: ec5cb13

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/kit Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@svelte-docs-bot
Copy link

@sillvva sillvva marked this pull request as draft October 26, 2025 21:19
@sillvva sillvva marked this pull request as ready for review October 26, 2025 21:51
@dummdidumm
Copy link
Member

thank you! I think something like this may make sense. I haven't talked with the other maintainers yet, but maybe we could go one step further and decouple all the client-side goodies from the core which is just about posting a form to the backend. As a result you would call some function, pass the remote function in, and can also pass initial values at the same time there, etc.

Could be nice for treeshaking and more involved scenarios but could also be a bit less ergonomic in the simple case (this one, too, btw), so we'll see.

@sillvva
Copy link
Contributor Author

sillvva commented Oct 28, 2025

That also sounds good. This was just the simplest change from the existing logic, thus making the migration easier. form to form()

@munxar
Copy link

munxar commented Oct 30, 2025

@dummdidumm Regarding ergonomics: I would say that the mental model for calling a function to create the form would feel more natural to me.
I ran into a problem when I create a form with default values and navigation parameters. Currently this would result in code like:

import { dataForm, getData } from './some.remote';
const { params } = $props();
// derived needed because of params.id dependency
const data = $derived(await getData(params.id));

// how to set the init data???
// 1. this would not be reactive
dataForm.fields.set(data)

// 2. this would not run server side
$effect(() => {
    dataForm.fields.set(data)
})

// 3. set every field by hand... very unhandy and doesn't scale
<input {...dataForm.fields.text.as("text")} value={data.text} />

The function call solves this, and feels more ergonomic to me.

import { dataForm, getData } from './some.remote';
const { params } = $props();
// get the data dependent on id
const data = $derived(await getData(params.id));
// create the form dependent on data
const form = $derived(dataForm({
   initialData: data
})

Another benefit of the factory: reduce manually setting attributes on the form like multipart:

<!-- current way -->
<form {...dataForm} enctype="multipart/form-data"></form>
<script>
import { dataForm, getData } from './some.remote';
const form = dataForm({
    // simpler for the user, and could be typed
    multipart: true
})
</script>
<!-- form can now set the correct enctype -->
<form {...form}></form>

@munxar
Copy link

munxar commented Oct 30, 2025

thank you! I think something like this may make sense. I haven't talked with the other maintainers yet, but maybe we could go one step further and decouple all the client-side goodies from the core which is just about posting a form to the backend. As a result you would call some function, pass the remote function in, and can also pass initial values at the same time there, etc.

Could be nice for treeshaking and more involved scenarios but could also be a bit less ergonomic in the simple case (this one, too, btw), so we'll see.

@dummdidumm

Another benefit of a stand alone client side form model would be, that we could use it with other datasources that are not remote functions. We write tons of SPAs where SvelteKit is used with adapter-static.
If SvelteKit could give a build in interface for the forms and fields it could be used with or without remote functions
and component libraries only need a little adapter to this interface.
I start to dream... ☺️

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remote form() result persists after navigation Allow resetting form based on remote function result.

4 participants