example

File Upload

If you want to append data also during precognitive validation request you could either:

  • set the corresponding option (validateFiles) in the UseFormOptions
  • call the form function form.validateFiles()

Example

here a snapshot of code that can:

  1. perfom clientSide validation.
  2. skip sending file during precognitive validation
  3. Use base nuxt $fetch to make http request to backend
/**
 * Post schema. Defined using Zod.
 * Add specific file validations because it is
 * sent during precognitive validation request. *
 */
const postSchema = z.object({
  title: z.string().min(5).max(100),
  content: z.string().min(10).max(1000),
  friends: z.array(z.string()),
  image: z.instanceof(File)
    .refine(file => file.size <= 10 * 1024 * 1024, {
      message: 'File size must be less than 10MB',
    })
    .refine(file => ['image/jpeg', 'image/png', 'image/gif'].includes(file.type), {
      message: 'File must be JPEG, PNG, or GIF',
    })
    .refine(file => file.name.length > 0, {
      message: 'File must have a name',
    })
    .refine(file => !!file.name.match(/^[a-z0-9]+\.[a-z0-9]+$/i), {
      message: 'File name must be alphanumeric with an extension. No spaces or special characters.',
    })
    .nullable(),
})
type Post = z.infer<typeof postSchema>

/**
 * Use the global $api to make backend request (to Laravel for example) defined in Nuxt Plugin.
 * It includes already Zod parser for validation errors.
 */
const { $api } = useNuxtApp()

/**
 * Define the form using useForm composable.
 * The second argument is a function to submit the form data to the backend. Being complete agnostic,
 * the headers to submit form data must be specified. Precognnitive header are already present.
 * Here we use FormData to handle file upload.
 * Client-side validation is specified in the UseFormOptions.
 */
const postForm = useForm(
  (): Post => ({ title: '', content: '', friends: [], image: null }),
  (data, headers) => {
    const formData = new FormData()
    formData.append('title', data.title)
    formData.append('content', data.content)
    data.friends.forEach((friend, index) => {
      formData.append(`friends[${index}]`, friend)
    })
    if (data.image) {
      formData.append('image', data.image)
    }
    headers.set('Content-Type', 'multipart/form-data')
    return $api(
      '/api/posts',
      {
        method: 'POST',
        headers,
        body: formData,
      }
    )
  },
  {
    clientValidation: postSchema.parse,
    validateFiles: true,
  },
)

function addImage(e: Event) {
  postForm.image = null
  postForm.forgetErrors('image')
  const input = e.target as HTMLInputElement
  if (input.files && input.files[0]) {
    postForm.image = input.files[0]
    postForm.validate('image')
  }
}

function handleSubmit() {
  postForm.submit({
    onBefore() {
      postForm.forgetErrors()
      return true
    },
    onStart: (data) => {
      postSchema.parse(data)
    }
  })
}
Previous
Base usage