File uploads

From version 2, Superforms has full support for file uploads. For that, you need a schema that can validate files. Zod has this possibility:

// NOTE: Import fail from Superforms, not from @sveltejs/kit!
import { superValidate, fail, message } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { z } from 'zod';

const schema = z.object({
  image: z
    .instanceof(File, { message: 'Please upload a file.'})
    .refine((f) => f.size < 100_000, 'Max 100 kB upload size.')
});

export const load = async () => {
  return { 
    form: await superValidate(zod(schema))
  }
};

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

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

    // TODO: Do something with the image
    console.log(form.data.image);

    return message(form, 'You have uploaded a valid file!');
  }
};

Returning files in form actions

Important: Because file objects cannot be serialized, you must return the form using fail, message or setError imported from Superforms:

import { message, setError, fail } from 'sveltekit-superforms';

// message, setError and the custom fail handles this automatically:
if (!form.valid) return fail(400, { form });
return message(form, 'Posted OK!');
return setError(form, 'image', 'Could not process file.');

Otherwise, a withFiles helper function is required:

// Importing the default fail
import { fail } from '@sveltejs/kit';
import { withFiles } from 'sveltekit-superforms';

// With the @sveltejs/kit fail:
if (!form.valid) {
  return fail(400, withFiles({ form }));
}

// When returning just the form:
return withFiles({ form })

This will remove the file objects from the form data, so SvelteKit can serialize it properly.

Examples

The recommended way to bind the file input to the form data is through a fileProxy or filesProxy, but you can also do it with an on:input handler. Here are examples for both, which also shows how to add client-side validation for files, which can save plenty of bandwidth!

Single file input

export const schema = z.object({
  image: z
    .instanceof(File, { message: 'Please upload a file.'})
    .refine((f) => f.size < 100_000, 'Max 100 kB upload size.')
});
<script lang="ts">
  import { superForm, fileProxy } from 'sveltekit-superforms'
  import { zodClient } from 'sveltekit-superforms/adapters'
  import { schema } from './schema.js'

  let { data } = $props();

  const { form, enhance, errors } = superForm(data.form, {
    validators: zodClient(schema)
  })

  const file = fileProxy(form, 'image')
</script>

<form method="POST" enctype="multipart/form-data" use:enhance>
  <input
    type="file"
    name="image"
    accept="image/png, image/jpeg"
    bind:files={$file}
  />
  {#if $errors.image}<span>{$errors.image}</span>{/if}
  <button>Submit</button>
</form>

Multiple files

We need an array to validate multiple files:

const schema = z.object({
  images: z
    .instanceof(File, { message: 'Please upload a file.'})
    .refine((f) => f.size < 100_000, 'Max 100 kB upload size.')
    .array()
});
<script lang="ts">
  import { superForm, filesProxy } from 'sveltekit-superforms'
  import { zodClient } from 'sveltekit-superforms/adapters'
  import { schema } from './schema.js'

  let { data } = $props();

  const { form, enhance, errors } = superForm(data.form, {
    validators: zodClient(schema)
  })

  const files = filesProxy(form, 'images');
</script>

<form method="POST" enctype="multipart/form-data" use:enhance>
  <input
    type="file"
    multiple
    name="images"
    accept="image/png, image/jpeg"
    bind:files={$files}
  />
  {#if $errors.images}<span>{$errors.images}</span>{/if}
  <button>Submit</button>
</form>

Validating files manually

If your validation library cannot validate files, you can obtain FormData from the request and extract the files from there, after validation:

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

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

    const image = formData.get('image');
    if (image instanceof File) {
      // Validate and process the image.
    }

    return withFiles({ form });
  }
};

If the file field isn’t a part of the schema, but you still want errors for it, you can add an optional field to the schema with the same name, and use setError to set and display an error message.

Progress bar for uploads

If you’d like a progress bar for large file uploads, check out the customRequest option for the onSubmit event.

Preventing file uploads

To prevent file uploads, set the { allowFiles: false } option in superValidate. This will set all files to undefined, so you don’t have to use withFiles.

This will also happen if you have migrated from version 1 and defined SUPERFORMS_LEGACY. In that case, set { allowFiles: true } to allow files.

Found a typo or an inconsistency? Make a quick correction here!