This is the documentation for Superforms version 1. The latest version can be found at superforms.rocks!

API reference

Throughout the reference, the following types are represented:

/**
 * T represents a validation schema.
 *
 * z.object({
 *   name: z.string().min(2)
 * })
 */
T extends AnyZodObject

/**
 * S refers to the underlying type of the schema,
 * the actual data structure.
 *
 * { name: string }
 */
S = z.infer<T>

/**
 * Nested represents a structure where the values in S
 * are replaced with the second parameter.
 *
 * { name: string[] | undefined }
 */
Nested<S, string[] | undefined> // Errors for each field in S

/**
 * FormPath and FormPathLeaves are string paths that points to a
 * field in the schema. FormPathLeaves can only be used at the
 * end nodes of the schema.
 *
 * z.object({
 *   tags: z.object({
 *     name: z.string().min(1)
 *   }).array()
 * })
 */
FormPath<S> = "tags[3]"
FormPathLeaves<S> = "tags[3].name"

Server API

import {
  superValidate,
  superValidateSync,
  actionResult,
  defaultValues,
  message,
  setError
} from 'sveltekit-superforms/server';

superValidate(schema | data, schema? | options?, options?)

If you want the form to be initially empty, you can pass the schema as the first parameter:

superValidate<T, M = any>(
  schema: T,
  options?: SuperValidateOptions
): Promise<SuperValidated<T, M>>

If you want to populate the form, for example, from a database, URL parameters in the load function, or FormData in the form actions, send the data as the first parameter and the schema second:

superValidate<T, M = any>(
  data:
    | RequestEvent
    | Request
    | FormData
    | URL
    | URLSearchParams
    | Partial<S>
    | null
    | undefined,
  schema: T,
  options?: SuperValidateOptions
): Promise<SuperValidated<T, M>>

The superValidateSync function can be useful on the client in Svelte components, since they cannot have top-level await.

superValidateSync<T, M = any>(
  data:
    | FormData
    | URL
    | URLSearchParams
    | Partial<S>
    | null
    | undefined,
  schema: T,
  options?: SuperValidateOptions
): SuperValidated<T, M>

superValidate options

SuperValidateOptions = {
  errors?: boolean;          // Add or remove errors from output (valid status is always preserved)
  id?: string;               // Form id, for multiple forms support
  preprocessed?: (keyof S)[] // Bypass superValidate data coercion for posted fields in this array
  warnings?: {               // Disable warnings
    multipleRegexps?: boolean;
    multipleSteps?: boolean;
  };
}

See the page about multiple forms for information about when to use id.

superValidate return type

SuperValidated<T, M = any> = {
  valid: boolean;
  posted: boolean;
  data: S;
  errors: Nested<S, string[] | undefined>;
  constraints: Nested<S, InputConstraints | undefined>;
  message?: M;
  id?: string;
};

If data is empty, a SuperValidated object with default values for the schema is returned, in this shape:

{
  valid: false;
  posted: false;
  errors: options.errors ? Nested<S, string[] | undefined> : {};
  data: S;
  constraints: Nested<S, InputConstraints>;
  id: undefined;
  message: undefined;
}

See this page for a list of default schema values.

Input constraints

/**
 * HTML input constraints returned from superValidate
 * Properties are mapped from the schema:
 */
InputConstraints = Partial<{
  required: boolean; // Not nullable(), nullish() or optional()
  pattern: string; // z.string().regex(r)
  min: number | string; // number if z.number.min(n), ISO date string if z.date().min(d)
  max: number | string; // number if z.number.max(n), ISO date string if z.date().max(d)
  step: number | 'any'; // z.number().step(n)
  minlength: number; // z.string().min(n)
  maxlength: number; // z.string().max(n)
}>;

setError(form, field, error, options?)

setError(
  form: SuperValidated<T, M>,
  field: '' | FormPathLeaves<S>,
  error: string | string[],
  options?: { overwrite = false, status : NumericRange<400, 599> = 400 }
) : ActionFailure<{form: SuperValidated<T, M>}>

For setting errors on the form after validation. It returns a fail(status, { form }) so it can be returned immediately, or more errors can be added by calling it multiple times before returning.

Use the overwrite option to remove all previously set errors for the field, and status to set a different status than the default 400 (which must be in the range 400-599).

  • To set a form-level error, the field argument can be skipped, or set to an empty string.
  • To set an array-level error, append ._errors to the field parameter, like "tags._errors".

message(form, message, options?)

message(
  form: SuperValidated<T, M>,
  message: M,
  options?: { status? : NumericRange<400, 599> }
) : { form: SuperValidated<T, M> } | ActionFailure<{form: SuperValidated<T, M>}>

message is a convenience method for setting form.message, best explained by an example:

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

export const actions = {
  default: async (event) => {
    const form = await superValidate<typeof schema, string>(event, 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 return fail and set form.valid = false, since status is >= 400
      return message(form, 'No spam please', {
        status: 403
      });
    }

    // Returns { form }
    return message(form, 'Valid form!');
  }
};

Note that the status option must be in the range 400-599.

defaultValues(schema)

Returns the default values for a schema, either the Superforms defaults or the ones you set on the schema yourself.

import { defaultValues } from 'sveltekit-superforms/server';
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(2),
  tags: z.string().min(1).array().default(['a', 'b'])
});

// Returns { name: '', tags: ['a', 'b'] }
const defaults = defaultValues(schema);

This corresponds to the form.data returned from const form = await superValidate(schema).

actionResult(type, data?, options? | status?)

When using an endpoint (a +server.ts file) instead of form actions, you must return an ActionResult to a form that has use:enhance applied. Anything else won’t work, not even throwing a redirect, since superForm expects an ActionResult.

The actionResult function helps you construct one in a Response object, so you can return a validation object from your API/endpoints.

import { actionResult } from 'sveltekit-superforms/server';

actionResult('success', { form }, 200);
actionResult('failure', { form }, 400);
actionResult('redirect', '/', 303);
actionResult('error', 'Error message', 500);

The default status codes for each result type are shown, so you don’t need to include them if they’re the same.

Additionally, the redirect version can send a flash message as a third parameter, in case you’re using flash messages. It can also set options for the flash cookie that’s being set.

actionResult('redirect', '/', {
  message: { type: 'success', text: 'Posted successfully!' },
  cookieOptions: { sameSite: 'lax' }
});

Login request example

src/routes/login/+server.ts

import { actionResult, superValidate } from '$lib/server';
import { z } from 'zod';

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(5)
});

export const POST = async ({ request }) => {
  const form = await superValidate(request, loginSchema);
  if (!form.valid) return actionResult('failure', { form });

  // Verify login here //

  return actionResult('success', { form });
};

Client API

import { superForm } from 'sveltekit-superforms/client';

The server part of the API can also be imported, for single-page app usage:

import {
  superValidate,
  superValidateSync,
  setError,
  setMessage, // Same as message
  actionResult,
  defaultValues
} from 'sveltekit-superforms/client';

superForm(form, options?)

superForm<T, M = any>(
  form: SuperValidated<T, M> | null | undefined,
  options?: FormOptions<T, M>
) : SuperForm<T, M>

superForm options

FormOptions<T, M> = Partial<{
  // Basics
  id: string;
  applyAction: boolean;
  invalidateAll: boolean;
  resetForm: boolean | (() => boolean);
  taintedMessage: string | false | null;
  dataType: 'form' | 'json';
  multipleSubmits: 'prevent' | 'allow' | 'abort';
  SPA: true | { failStatus?: number };

  // Error handling
  scrollToError: 'auto' | 'smooth' | 'off' | boolean | ScrollIntoViewOptions;
  autoFocusOnError: boolean | 'detect';
  errorSelector: string;
  selectErrorText: boolean;
  stickyNavbar: string;

  // Events
  onSubmit: (
    ...params: Parameters<SubmitFunction>
  ) => MaybePromise<unknown | void>;
  onResult: (event: {
    result: ActionResult;
    formEl: HTMLFormElement;
    cancel: () => void;
  }) => MaybePromise<unknown | void>;
  onUpdate: (event: {
    form: SuperValidated<T, M>;
    formEl: HTMLFormElement;
    cancel: () => void;
  }) => MaybePromise<unknown | void>;
  onUpdated: (event: {
    form: Readonly<SuperValidated<T, M>>;
  }) => MaybePromise<unknown | void>;
  onError:
    | 'apply'
    | ((event: {
        result: {
          type: 'error';
          status?: number;
          error: App.Error;
        };
        message: Writable<SuperValidated<T, M>['message']>;
      }) => MaybePromise<unknown | void>);

  // Client-side validation
  validators: T | false | Validators<T>;
  validationMethod: 'auto' | 'oninput' | 'onblur' | 'submit-only';
  defaultValidator: 'keep' | 'clear';
  clearOnSubmit: 'errors' | 'message' | 'errors-and-message' | 'none';
  delayMs: number;
  timeoutMs: number;

  // Flash message integration
  syncFlashMessage?: boolean;
  flashMessage: {
    module: import * as flashModule from 'sveltekit-flash-message/client';
    onError?: (event: {
      result: {
        type: 'error';
        status?: number;
        error: App.Error;
      };
      message: Writable<App.PageData['flash']>;
    }) => MaybePromise<unknown | void>;
    cookiePath?: string;
    cookieName?: string;
  };

  // Disable warnings
  warnings: {
    duplicateId?: boolean;
    noValidationAndConstraints?: boolean;
  };
}>;

See SubmitFunction for details about the onSubmit arguments, and ActionResult for onResult.

superForm return type

SuperForm<T extends AnyZodObject, M = any> = {
  form: {
    subscribe: (data: S) => void
    set: (value: S, options?: { taint?: boolean | 'untaint' | 'untaint-all' }) => void
    update: (updater: (S) => S, options?: { taint?: boolean | 'untaint' | 'untaint-all' }) => void
  };
  errors: Writable<Nested<S, string[] | undefined>>;
  constraints: Writable<Nested<S, InputConstraints | undefined>>;
  message: Writable<M | undefined>;
  tainted: Writable<Nested<S, boolean | undefined> | undefined>;

  submitting: Readable<boolean>;
  delayed: Readable<boolean>;
  timeout: Readable<boolean>;
  posted: Readable<boolean>;

  formId: Writable<string | undefined>;
  fields: Record<keyof S, FormField<T>>;
  allErrors: Readable<{ path: string; messages: string[] }[]>;

  options: FormOptions<T, M>;

  enhance: (el: HTMLFormElement, events?: {
    onSubmit, onResult, onError, onUpdate, onUpdated
  }) => ReturnType<typeof $app/forms/enhance>;

  reset: (options?: {
    keepMessage?: boolean;
    id?: string;
    data?: Partial<S>;
  }) => void;

  capture: () => SuperFormSnapshot<T, M>;
  restore: (snapshot: SuperFormSnapshot<T, M>) => void;

  validate: (path?: FormPathLeaves<S>, opts?: {
    value: FormPathType<FormPathLeaves<S>>;
    update: boolean | 'errors' | 'value';
    taint: boolean | 'untaint' | 'untaint-all';
    errors: string | string[];
  }) => Promise<(string[] | undefined) | SuperValidated<T, M>>;
};

FormField<S, Prop extends keyof S> = {
  readonly name: Prop;
  value: Writable<S[Prop]>;
  errors?: Writable<ValidationErrors<S[Prop]>>;
  constraints?: Writable<InputConstraints<S[Prop]>>;
};

Proxy objects

import {
  // The first ones uses the $form store
  // and is always a Writable<string>:
  booleanProxy,
  dateProxy,
  intProxy,
  numberProxy,
  stringProxy,
  // Uses the whole object returned from superForm.
  // Type depends on the field.
  formFieldProxy,
  arrayProxy,
  // Can use any object. Type depends on the field.
  fieldProxy      
} from 'sveltekit-superforms/client';

A proxy handles bi-directional updates and data transformation of a corresponding form field. Updates in either the proxy or data it points to, will reflect in the other.

intProxy(form, fieldName, options?)

Creates a string store for an integer field in the schema. It’s rarely needed as Svelte handles this automatically with bind:value.

import { superForm, intProxy } from 'sveltekit-superforms/client';
export let data;

const { form } = superForm(data.form);
const proxy = intProxy(form, 'field', { options });

Options:

{ 
  empty?: 'null' | 'undefined';
  emptyIfZero?: boolean = true;
}

Use the empty option to set the field to null or undefined if the value is falsy. (Which includes the number zero, unless emptyIfZero is set to false.)

numberProxy(form, fieldName, options?)

Creates a string store for a number field in the schema. It’s rarely needed as Svelte handles this automatically with bind:value.

import { superForm, numberProxy } from 'sveltekit-superforms/client';
export let data;

const { form } = superForm(data.form);
const proxy = numberProxy(form, 'field', { options });

Options:

{ 
  empty?: 'null' | 'undefined';
  emptyIfZero?: boolean = true;
  delimiter?: '.' | ',';
}

Use the empty option to set the field to null or undefined if the value is falsy. (Which includes the number zero, unless emptyIfZero is set to false.)

booleanProxy(form, fieldName, options?)

Creates a string store for a boolean schema field. The option can be used to change what string value should represent true. An empty string always represents false.

import { superForm, booleanProxy } from 'sveltekit-superforms/client';
export let data;

const { form } = superForm(data.form);
const proxy = booleanProxy(form, 'field', { options });

Options:

{
  trueStringValue = 'true';
}

dateProxy(form, fieldName, options?)

Creates a string store for a Date schema field. The option can be used to change the proxied string format of the date.

import { superForm, dateProxy } from 'sveltekit-superforms/client';
export let data;

const { form } = superForm(data.form);
const proxy = dateProxy(form, 'field', { options });

Options:

{
  format:
    // Extract the part of the date as a substring:
    | 'date' | 'datetime' | 'time'
    // Convert the date to UTC:
    | 'date-utc' | 'datetime-utc' | 'time-utc'
    // Convert the date to local time:
    | 'date-local' | 'datetime-local' | 'time-local'
    // The default ISODateString:
    | 'iso' = 'iso';
  empty?: 'null' | 'undefined'
}

stringProxy(form, fieldName, options)

Creates a string store for a string schema field. It may look redundant, but the option can be useful if you need to convert an empty string to null or undefined.

import { superForm, stringProxy } from 'sveltekit-superforms/client';
export let data;

const { form } = superForm(data.form);
const proxy = stringProxy(form, 'field', { options });

Options:

{
  empty: 'null' | 'undefined';
}

formFieldProxy(superForm, fieldName, options)

Proxies a form field, returning stores similar to superForm but for a single field. For arrays in the schema, see below for how to create an arrayProxy.

<script lang="ts">
  import { superForm, formFieldProxy } from 'sveltekit-superforms/client';

  export let data;

  const theForm = superForm(data.form); // The whole superForm object is required
  const { form } = theForm; // Deconstruct as usual here

  const { path, value, errors, constraints, tainted } = formFieldProxy(theForm, 'name');
</script>

Options:

{
  taint: boolean | 'untaint' | 'untaint-all' = true;
}

The option can be used to prevent tainting the form when modifying the proxy.

For more details about formFieldProxy, see the components page.

arrayProxy(superForm, fieldName, options)

Proxies an array in a form, returning stores similar to superForm but for the array.

<script lang="ts">
  import { superForm, arrayProxy } from 'sveltekit-superforms/client';

  export let data;

  const theForm = superForm(data.form); // The whole superForm object is required
  const { form } = theForm; // Deconstruct as usual here

  const { path, values, errors, fieldErrors } = arrayProxy(theForm, 'tags');
</script>
  • errors displays errors for the array itself, for example if the number of items are too few.
  • fieldErrors is an array that lists errors for the contents of the array.

Options:

{
  taint: boolean | 'untaint' | 'untaint-all' = true;
}

The option can be used to prevent tainting the form when modifying the proxy.

An example of how to use arrayProxy in a component is available on Stackblitz.

fieldProxy(object, fieldName)

Proxies field access in any object, usually in $form, but in that case formFieldProxy and arrayProxy are more convenient.

<script lang="ts">
  import { superForm, fieldProxy } from 'sveltekit-superforms/client';
  export let data;

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

  // Proxy any field in an object
  const nameProxy = fieldProxy(form, 'name');
</script>

Proxy example

Given the following schema:

const schema = z.object({
  date: z.date()
});

A proxy for a HTML date field can be used like this:

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

  export let data: PageData;

  const { form, enhance } = superForm(data.form);
  const date = dateProxy(form, 'date', { format: 'date', empty: 'undefined' });
</script>

<input name="date" type="date" bind:value={$date} />

Components

SuperDebug

SuperDebug is a must-have debugging component that gives you colorized and nicely formatted output for any data structure, usually $form.

More information and usage examples can be found on the SuperDebug page.