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

Multiple forms on the same page

Since there is only one page store for each +page.svelte, having multiple forms, for example a register and login form on the same page, can cause problems since any form action will update $page.form and possibly affect both forms.

Fortunately Superforms has a solution for this: Multiple forms on the same page are possible by setting options.id for each form (one can have the default id, undefined). This prevents them from interfering with each other.

Usage

const form = await superValidate(schema, {
  id: string | undefined
});

By setting an id like this on the server, you’ll ensure that only forms with the matching id on the client will react on the updates:

+page.server.ts

import { z } from 'zod';
import { fail } from '@sveltejs/kit';
import { message, superValidate } from 'sveltekit-superforms/server';

const loginSchema = z.object({
  name: z.string().min(1)
});

const registerSchema = z.object({
  name: z.string().min(1)
});

export const load = async () => {
  const loginForm = await superValidate(loginSchema, {
    id: 'loginForm'
  });

  const registerForm = await superValidate(registerSchema, {
    id: 'registerForm'
  });

  return { loginForm, registerForm };
};

export const actions = {
  login: async ({ request }) => {
    const loginForm = await superValidate(request, loginSchema, {
      id: 'loginForm'
    });

    if (!loginForm.valid) return fail(400, { loginForm });
    return message(loginForm, 'Login form submitted');
  },
  register: async ({ request }) => {
    const registerForm = await superValidate(request, registerSchema, {
      id: 'registerForm'
    });

    if (!registerForm.valid) return fail(400, { registerForm });
    return message(registerForm, 'Register form submitted');
  }
};

Server-client communication

This is only the server-to-client part however. As you can post to this form action from anywhere, the server knows nothing about who sent the form.

The example above is using named form actions to determine which form was posted. On the client, you’ll post to these different form actions for the respective form:

+page.svelte

<script lang="ts">
  import { superForm } from 'sveltekit-superforms/client';
  import type { PageData } from './$types';

  export let data: PageData;

  const { form, errors, enhance, message } = superForm(data.loginForm, {
    invalidateAll: false
  });

  const {
    form: registerForm,
    errors: registerErrors,
    enhance: registerEnhance,
    message: registerMessage
  } = superForm(data.registerForm, {
    invalidateAll: false
  });
</script>

{#if $message}<h3>{$message}</h3>{/if}

<form method="POST" action="?/login" use:enhance>
  Name: <input type="text" name="name" bind:value={$form.name} />
  <button>Submit</button>
  {#if $errors.name}
    <br /><span class="invalid">{$errors.name}</span>
  {/if}
</form>

<hr />

{#if $registerMessage}<h3>{$registerMessage}</h3>{/if}

<form method="POST" action="?/register" use:registerEnhance>
  Name: <input type="text" name="name" bind:value={$registerForm.name} />
  <button>Submit</button>
  {#if $registerErrors.name}
    <br /><span class="invalid">{$registerErrors.name}</span>
  {/if}
</form>

This works well with forms that only post to its dedicated form action. But for more dynamic scenarios, let’s say a database table where rows can be editable, you want to communicate to the server which form id was sent. This can be done with a hidden form field or a query parameter, to let the server know what id was posting, and what it should respond with.

Setting id on the client

On the client, the id is picked up automatically when you pass data.form to superForm. But if you don’t send any data to the form initially, you can set the id directly in the first argument to superForm:

// The default: If data is used, the id is sent along with it.
const { form, enhance } = superForm(data.loginForm, schema);

// If no data is sent (for empty forms), the id can be used directly.
const { form, enhance } = superForm('loginForm', schema);

// In advanced cases, you can set the id as an option.
// It will take precedence over data.form.id.
const { form, enhance } = superForm(data.form, schema, {
  id: 'special-id'
});

Configuration and troubleshooting

Due to the many different use cases, it’s hard to set sensible defaults for multiple forms. A common issue is that the other forms’ data are lost when one is submitted. This is due to the page being invalidated as default on a successful response. If you want to preserve their data, you’d almost certainly want to set invalidateAll: false or applyAction: false on them, as in the example above. The use:enhance options explains the differences between them.

Also check out the componentization page for ideas on how to place the forms into separate components, to make +page.svelte less cluttered.

Test it out

Here we have two forms, with separate id’s and two extra options:

{
  // Clear form on success.
  resetForm: true,
  // Prevent page invalidation, which would clear the
  // other form when the load function executes again.
  invalidateAll: false
}

Submit them and see that they are acting independently.

Register user

Login