Guides / File Upload

File Upload

To handle file uploads, the form encType attribute must be set to multipart/form-data and the method must be POST.

#Configuration

Setting up a file input is no different from other inputs.

1import { useForm } from '@conform-to/react';
2import { parseWithZod } from '@conform-to/zod';
3import { z } from 'zod';
4
5const schema = z.object({
6  profile: z.instanceof(File, { message: 'Profile is required' }),
7});
8
9function Example() {
10  const [form, fields] = useForm({
11    onValidate({ formData }) {
12      return parseWithZod(formData, { schema });
13    },
14  });
15
16  return (
17    <form method="POST" encType="multipart/form-data" id={form.id}>
18      <div>
19        <label>Profile</label>
20        <input type="file" name={fields.profile.name} />
21        <div>{fields.profile.error}</div>
22      </div>
23      <button>Upload</button>
24    </form>
25  );
26}

#Multiple files

To allow uploading multiple files, you need to set the multiple attribute on the file input. It is important to note that the errors on the field metadata might not include all the errors on each file. As the errors from both yup and zod are mapped based on the corresponding paths and the errors of each file will be mapped to its corresponding index, e.g. files[0] instead of the array itself, e.g. files. If you want to display all the errors, you can consider using the allErrors property on the field metadata instead.

1import { useForm } from '@conform-to/react';
2import { parse } from '@conform-to/zod';
3import { z } from 'zod';
4
5const schema = z.object({
6  files: z
7    .array(
8      z
9        .instanceof(File)
10        .refine((file) => file.size < 1024, 'File size must be less than 1kb'),
11    )
12    .min(1, 'At least 1 file is required')
13    .refine(
14      (files) => files.every((file) => file.size < 1024),
15      'File size must be less than 1kb',
16    ),
17});
18
19function Example() {
20  const [form, fields] = useForm({
21    onValidate({ formData }) {
22      return parseWithZod(formData, { schema });
23    },
24  });
25
26  return (
27    <form method="POST" encType="multipart/form-data" id={form.id}>
28      <div>
29        <label>Mutliple Files</label>
30        <input type="file" name={fields.files.name} multiple />
31        <div>
32          {Object.entries(fields.files.allErrors).flatMap(
33            ([, messages]) => messages,
34          )}
35        </div>
36      </div>
37      <button>Upload</button>
38    </form>
39  );
40}