This is the legacy documentation for Superforms 0.x. The 1.0 version can be found at superforms.rocks!

Error handling

By deconstructing errors from superForm, you’ll get an object with form errors that you can display where it’s appropriate:

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

<form method="POST">
  <label for="name">Name</label>
  <input name="name" data-invalid={$errors.name} bind:value={$form.name} />
  {#if $errors.name}<span class="invalid">{$errors.name}</span>{/if}

  <div><button>Submit</button></div>
</form>

The data-invalid attribute is used to automatically focus on the first error field, see the errorSelector option further below.

setError

Most errors will be set automatically when the data is validated, but you may want to add errors after determining that the data is valid. This is easily done with the setError helper function.

import { setError, superValidate } from 'sveltekit-superforms/server';

export const actions = {
  default: async ({ request }) => {
    const form = await superValidate(request, schema);

    if (!form.valid) {
      return fail(400, { form });
    }

    if (db.users.find({ where: { email: form.data.email } })) {
      return setError(form, 'email', 'E-mail already exists.');
    }

    return { form };
  }
};

setError returns a fail(400, { form }) so it can be returned immediately, or more errors can be added by calling it multiple times before returning. See the API for more options.

If you have nested data, you’ll use an array to specify where in the data structure the error is:

// Error in post.tags[3]
setError(form, ['post', 'tags', 3], 'Invalid tag name.');

Usage (client)

As said, errors are available in the $errors store. It gives you a high flexibility, since you can place error messages anywhere on the page.

In larger forms, the submit button may be far away from the error, so it’s nice showing the user where the first error is. There are a couple of options for that:

const { form, enhance, errors, allErrors } = superForm(data.form, {
  errorSelector: string | undefined = '[data-invalid]'
  scrollToError: 'smooth' | 'auto' | 'off' = 'smooth'
  autoFocusOnError: boolean | 'detect' = 'detect'
  stickyNavbar: string | undefined = undefined,
  onError: (({ result, message }) => void) | 'apply'
})

errorSelector

This is the CSS selector used to locate the invalid input fields after form submission. The default is [data-invalid], and the first one found on the page will be scrolled to and focused on, depending on the other settings. You usually set it on the input fields as such:

<input
  type="email"
  name="email"
  bind:value={$form.email}
  data-invalid={$errors.email} />

scrollToError

The scrollToError options determines how to scroll to the first error message in the form. smooth and auto are values from Window.scroll().

autoFocusOnError

When autoFocusOnError is set to its default value detect, it checks if the user is on a mobile device, if not it will automatically focus on the first error input field. It’s prevented on mobile since auto-focusing will open the on-screen keyboard, most likely hiding the validation error.

If you have a sticky navbar, set its selector here and it won’t hide any errors due to its height and z-index.

onError

This option is an event, explained more in detail on the event page, but it’s mentioned here since you should usually implement this to display server errors in a user-friendly way. It can be as simple as this:

// result is an error ActionResult
// message is the form $message store
onError({ result, message }) {
  message.set(result.error.message);
}

Errors can be thrown server-side with the SvelteKit error helper:

import { error } from '@sveltejs/kit';

export const actions = {
  default: async ({ request }) => {
    const form = await superValidate(request, schema);

    try {
      db.insert(form.data);
    } catch (e) {
      throw error(500, 'Something went wrong, please try again.');
    }
  }
};

Form-level errors

It’s also possible to set form-level errors, either through refining the schema, or the setError function.

const refined = z
  .object({
    name: z.string().min(1)
  })
  .refine((data) => request.method == 'POST', 'Invalid request method.');

const form = await superValidate(request, refined);

return setError(form, null, 'Form-level problem.');

These can be accessed on the client through $errors._errors.

Listing errors

You may also want to list the errors above the form. The $allErrors store can be used for this. It’s an array that contains all errors and their field names:

{#if $allErrors.length}
  <ul>
    {#each $allErrors as error}
      <li>
        <b>{error.path}:</b>
        {error.message}
      </li>
    {/each}
  </ul>
{/if}

This can also be useful to disable the submit button if there are any errors.

Test it out

This form has data-invalid set on erroneous fields, and lists all errors on top of the form using $allErrors. Try to submit and see that the first error field gets focus automatically, unless on mobile.