Progressive enhancement

By using enhance returned from superForm, we’ll get the client-side enhancement expected from a modern website:

<script lang="ts">
  const { form, enhance } = superForm(data.form);
  //            ^^^^^^^

<form method="POST" use:enhance>

Now all form submissions will happen on the client, and we unlock lots of client-side features like events, timers, auto error focus, etc.

The use:enhance action takes no arguments; instead, events are used to hook into the default SvelteKit use:enhance parameters and more. Check out the events page for details.

Differences from SvelteKit’s use:enhance

There are three notable differences between the Superforms and SvelteKit enhance.

1. ActionResult errors are changed to failure

Any ActionResult with status error is transformed into failure to avoid form data loss. The SvelteKit default is to render the nearest +error.svelte page, which will wipe out the form and all data that was just entered. Returning fail with a status message or using the onError event is a more user-friendly way of handling server errors.

2. The form isn’t resetted by default

Resetting the form is disabled as default to avoid accidental data loss. It’s not always wanted as well, for example in backend interfaces, where the form data should be kept after updating.

It’s easy to enable though, read further down at the resetForm option for details.

3. A tainted form warning is added

Tainted fields is a feature which shows a dialog to the user when navigating away from a modified (“tainted” or “dirty”) form. This is enabled by default, again to avoid data loss for the user.


If you want to modify the basic use:enhance behavior, here are the options available along with the default values; you don’t need to add them unless you want to change a value.

const { form, enhance, reset } = superForm(data.form, {
  applyAction: true,
  invalidateAll: true,
  resetForm: false

When to change the defaults?

Quite rarely! If you have a single form on the page and nothing else is causing the page to invalidate, you’ll probably be fine as it is. For multiple forms on the same page, you have to experiment with these three options. Read more on the multiple forms page.

applyAction and invalidateAll are the most technical ones; if you’re dealing with a single form per page, you can most likely skip them.


When applyAction is true, the form will have the default SvelteKit behavior of both updating and reacting on $page.form and $page.status, and also redirecting automatically.

Turning this behavior off can be useful when you want to isolate the form from other sources updating the page, for example Supabase events, a known source of confusing form behavior. Read more about applyAction in the SvelteKit docs.


When invalidateAll is true (the default) and a successful validation result is returned from the server, the page will be invalidated and the load functions will run again. A login form takes advantage of this to update user information on the page.


When true, reset the form upon a successful validation result.

Note however, that since we’re using bind:value on the input fields, a HTML form reset (clearing all fields in the DOM) won’t have any effect. So in Superforms, resetting means going back to the initial state of the form data, basically setting $form to what was initially sent to superForm.

For a custom reset, you can instead modify the data field returned from superValidate on the server, or use the events together with the reset function on the client.

Making the form behave like the SvelteKit default

You can remove the differences described above by setting the following options. Use with care, since the purpose of the changes is to protect the user from data loss.

const { form, enhance } = superForm(data.form, {
  // Reset the form upon a successful result
  resetForm: true,
  // On ActionResult error, render the nearest +error.svelte page
  onError: 'apply',
  // No message when navigating away from a modified form
  taintedMessage: null