Skip to content

UForm dirty state can become true again after successful submit due to debounced input event #6397

@CallumWatkins

Description

@CallumWatkins

Environment

  • Operating System: Windows 10.0.26200
  • Node Version: v24.13.1
  • Nuxt Version: 4.2.2
  • CLI Version: 3.31.1
  • Nitro Version: 2.12.9
  • Package Manager: bun@1.3.6
  • Builder: vite@7.2.6
  • User Config: app, build, compatibilityDate, css, devServer, devtools, echarts, eslint, experimental, i18n, ignore, imports, modules, router, ssr, telemetry, vite
  • Runtime Modules: @vueuse/nuxt@14.1.0, @nuxt/ui@4.3.0, reka-ui/nuxt@2.6.1, @nuxt/eslint@1.12.1, nuxt-echarts@1.0.1, @nuxtjs/i18n@10.2.3
  • Build Modules: -

Is this bug related to Nuxt or Vue?

Nuxt

Package

v4.x

Version

v4.3.0

Reproduction

https://play.ui.nuxt.com/#eNptU8Fu2zAM/RXOhzoF5viwnQI36zaswC7DgGCneQfFplMBtixIlNEg8L+Pki1lWXuySD4+ko/0Jfus9XZymO2yyjZGagKL5DT0Qp0e6oxsne1rJQc9GoILGBQNyQnf86uDGTozDpAzQZ5AdNbIyKfRDAd3HCR9m1BRwj4q90Klk5xQq4A9NM84CHjgJCUG3IElI9UJZo9oRmUJOiZjANfc3EefJUEYnEtL1U9hSIq+Wvj2+03ky3OYr2liwvYH+1e+PE+hVrYHjq6BTvQWOVYrYc+qgc4prjMqGNUy1wb9YLv/J4317+FSK4ik20n0zlMHWh9IjaRQ4Nu2gsTWN/5WNhnHflamKpd18XLYIBx0z3KwBVD98h35F/hBeItevjpbPLugGzvDN3m5gOTKWIyKY7/zY+9M/ieGm15Y63O0aLA4Fx9j4NGGwTkUVVkioZHYypPEvuWLOmLPQD9ynYXVsBUG9Sfm4T7hu9KOYCqGsQ3w0OaiRwZl5C2vxEGB4GzllDrtenwBybrYomFV0cBJ6OLDTakvjoj36Y/QF1rbTwAAL32Clys+1WMf6+HtiG+lofMOLpdwsJ+2wYaZ1xU51oQbAh5W+i3F87u7g3f/5N909PWZ/0u0y/G8ol0MluGqiTa4T5cWWrv+APNclT4ejmZRlN9Vma4pm/8CBZ9c5A==

Steps to reproduce::

  1. Type into the Name field.
  2. Click Save (or press enter) within 300 ms of the last keypress.
  3. Observe that the submit handler receives and saves the correct value.
  4. Observe that "Changes saved" appears briefly or not at all.
  5. Observe that form.dirty becomes true again after the successful submit.

If you wait more than 300 ms before submitting, the issue does not happen.

Description

UForm can become dirty again after a successful submit when the user submits shortly after typing into a UInput.

This appears to happen because UInput emits the form input event through useDebounceFn, using validateOnInputDelay from UForm. The default delay is 300.

UForm uses that same debounced input event for dirty tracking:

if (event.type === 'change' || event.type === 'input') {
  dirtyFields.add(event.name)
}

On successful submit, UForm clears dirty state:

await props.onSubmit?.(event)
dirtyFields.clear()

When submit completes before the pending debounced input event fires, the following happens:

  1. UInput updates the bound model immediately.
  2. The form input event is scheduled with the default 300 ms debounce.
  3. The user submits quickly.
  4. UForm validates and calls onSubmit.
  5. onSubmit succeeds.
  6. UForm calls dirtyFields.clear().
  7. The previously scheduled debounced input event fires.
  8. UForm handles that stale input event and adds the field back to dirtyFields.
  9. form.dirty becomes true even though the current form value was just successfully submitted.

This causes issues for patterns such as:

<span v-if="didSave && !form?.dirty">Changes saved</span>

and:

onBeforeRouteLeave(() => {
  if (form.value?.dirty) {
    // warn about unsaved changes
  }
})

A local workaround is to set:

<UForm :validate-on-input-delay="0" />

but that works by disabling the debounce entirely, which is not always desirable.

Additional context

This appears related to issue #5700 that was closed as stale. That issue reported debounced UFormField validation firing after a fast submit/reset/clear() and showing stale errors.

Logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageAwaiting initial review and prioritizationv4#4488

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions