Status messages

A status message like “Form posted” can be displayed after submitting a form. The validation object contains a message store used for this:

Usage

const { form, message } = superForm(data.form);

It is used to display the message on the client, like any other store:

{#if $message}
  <div class="message">{$message}</div>
{/if}

However, we need to send it from the server first. Using the message function makes this rather simple.

The message helper

import { message, superValidate } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';

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

    if (!form.valid) {
      // Will return fail(400, { form }) since form isn't valid
      return message(form, 'Invalid form');
    }

    if (form.data.email.includes('spam')) {
      // Will also return fail, since status is >= 400
      // form.valid will also be set to false.
      return message(form, 'No spam please', {
        status: 403
      });
    }

    // Just returns { form } with the message (and status code 200).
    return message(form, 'Valid form!');
  }
};

You can return any type with the message, like an object, if you want to send more information than a string:

return message(form, { text: 'User created', id: newId })

See right below for how to make this data strongly typed.

Strongly typed message

The message is of type any by default, but you can type it using the superValidate type parameters:

import { type Infer, superValidate } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';

// Inferred schema type as first parameter, message type second
const form = await superValidate<Infer<typeof schema>, string>(event, zod(schema));

A string can be a bit limiting though; more realistically, there will be some kind of status for the form submission, so making a Message type can be useful for consistency.

import { type Infer, superValidate } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';

type Message = { status: 'error' | 'success' | 'warning'; text: string };

const form = await superValidate<Infer<typeof schema>, Message>(event, zod(schema));

To simplify this even further, if you have the same type for all status messages across the project, you can add a Message type to the App.Superforms namespace in src/app.d.ts, and it will be automatically set, no need for generic type parameters:

src/app.d.ts

declare global {
  namespace App {
    // interface Error {}
    // interface Locals {}
    // interface PageData {}
    // interface Platform {}
    namespace Superforms {
      type Message = {
        type: 'error' | 'success', text: string
      }
    }
  }
}

src/routes/+page.svelte

<script lang="ts">
  import type { PageData } from './$types.js';
  export let data: PageData;

  const { form, message } = superForm(data.form);
</script>

{#if $message}
  <div 
    class:success={$message.status == 'success'} 
    class:error={$message.status == 'error'}
  >
    {$message.text}
  </div>
{/if}

Though if you want to keep it simple with a string or the default any, you can use $page.status to style the message appropriately:

<script lang="ts">
  import { page } from '$app/stores';
  import type { PageData } from './$types.js';

  export let data: PageData;

  const { form, message } = superForm(data.form);
</script>

{#if $message}
  <div 
    class:success={$page.status == 200} 
    class:error={$page.status >= 400}
  >
    {$message}
  </div>
{/if}

Using the message data programmatically

If you return data that you want to use programmatically instead of just displaying it, like in a toast message, you can do that in the onUpdate or onUpdated event:

return message(form, { status: 'success', text: 'All went well' });
const { form, enhance } = superForm(data.form, {
  onUpdated({ form }) {
    if (form.message) {
      // Display the message using a toast library
      toast(form.message.text, {
        icon: form.message.status == 'success' ? '✅' : '❌'
      });
    }
  }
});

The difference between the two events is that you can modify and cancel the update in onUpdate, compared to onUpdated, where the form data, errors, etc have already updated, making it best for non-store-related things like displaying a toast.

Limitations

Since there is no form data returned when redirecting, in that case the message will be lost. It’s quite common to redirect after a successful post, so the message property isn’t a general solution for displaying status messages.

The library sveltekit-flash-message is a complete solution that works with redirects, however. It can be directly integrated into Superforms, documented here.