21

Submission and reset

Submit validated data, handle errors thrown by your submit handler, validate without submitting, and reset values, errors, and state.

Submit with Form

Form renders a native <form>. It prevents the browser's page navigation on submit, calls form.submit, and handles native reset by calling form.reset.

<template>
  <Form :form="form">
    <!-- fields -->
    <button
      type="submit"
      :disabled="form.isSubmitting"
    >
      {{ form.isSubmitting ? 'Saving...' : 'Save' }}
    </button>

    <button
      type="reset"
      :disabled="form.isSubmitting"
    >
      Reset
    </button>
  </Form>
</template>

What a submit does

  1. A submit while another is still running is ignored.
  2. isSubmitting becomes true, submitCount increases, and every field counts as touched.
  3. The whole form is validated.
  4. If a value changed while validation was running, validation runs again on the new values.
  5. If the schema reports issues, they replace all errors and onSubmit is not called. isSubmitted is still true.
  6. If validation passes, errors are cleared and onSubmit is awaited with the validated output.
  7. isSubmitting becomes false again, even if onSubmit throws.

onSubmit can be sync or async. isSubmitting stays true for the whole duration, so you can use it to disable the button or show a spinner.

const form = useForm({
  initialValues,
  async onSubmit(data) {
    await api.save(data) // data is SchemaOutput<typeof schema>
  },
  schema,
})

Errors thrown in onSubmit

If onSubmit throws or rejects, isSubmitting is reset and the error propagates out of form.submit().

  • When Form triggers the submit, the rejection goes to Vue's error handler (app.config.errorHandler, or vue:error in Nuxt) and your UI gets no error message.
  • When you call form.submit() yourself, the returned promise rejects.

Handle expected failures inside onSubmit and turn them into form errors:

const form = useForm({
  async onSubmit(data) {
    try {
      await api.save(data)
    } catch {
      form.setError({ message: 'We could not save your changes. Try again.' })
    }
  }
})

The Server errors recipe shows how to map field-level responses as well.

Submit programmatically

Call form.submit() when a submit does not start from <Form>, for example from a keyboard shortcut or a button outside the form element. It validates before invoking onSubmit.

await form.submit()

Passed a native submit event, it also calls preventDefault(). If you do not want to use Form, this is the equivalent markup:

<template>
  <form
    @submit="form.submit"
    @reset.prevent="form.reset"
  >
  <!-- fields -->
  </form>
</template>

Validate without submitting

form.validate() validates the whole form, updates form.errors, and does not call onSubmit.

const result = await form.validate()

if (result.issues) {
  // show a message, scroll to the first error, etc.
} else {
  result.value // parsed output
}

Reset the form

form.reset() returns the form to its baseline:

  • values return to the current initialValues;
  • errors return to the current initialErrors;
  • touched state and submitCount are cleared, so isTouched and isSubmitted become false;
  • pending debounced validations and in-flight validations are cancelled, along with any submit in progress;
  • array items receive fresh identities, and resetCount increases.

Use type="reset" inside <Form>, or call form.reset() yourself.

Reset restores the baseline, not an empty form. If the baseline holds loaded data, reset returns to that data.

Replace the baseline

Pass values, errors, or both to start from a new baseline. The values you supply replace the old baseline. They are not merged with it. An omitted argument keeps its current baseline.

form.reset(
  { email: 'jane@example.com' },
  [{ message: 'Verify this address.', path: ['email'] }],
)

The next plain form.reset() returns to this new baseline. Use this when saved data should become the new starting point for dirty tracking. See Editing existing data.

Remount inputs after reset

form.resetCount increases on every reset. Use it as a :key on a subtree whose uncontrolled state should be discarded.

<template>
  <MyThirdPartyEditor
    :key="form.resetCount"
    v-model="form.values.body"
  />
</template>

Reset after a successful submit

NotForm does not reset on its own after onSubmit. Do it when it suits your flow:

const form = useForm({
  async onSubmit(data) {
    await api.create(data)
    form.reset() // back to a blank form
  // or: form.reset(data) to make the saved data the new baseline
  }
})

Next steps

Copyright © 2026 NotForm
Published under MIT License