nuxt / nuxt/ui

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

Open
#6,397 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

triage
Dominant language
TypeScript
Stars
6.9k
Forks
1.1k
Avg merge
1d 7h
Merged PRs (30d)
57

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the UForm dirty-tracking logic and the UInput debounced form input behavior described in the issue, then reproduce the sequence in the linked UI sandbox. Done means a successful submit within 300 ms of typing leaves form.dirty false after the pending event, while the debounce remains available for normal input validation.

Written by the indexing model from the issue text.

Assessment

Tech stack
nuxt, typescript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.