Submission — Pending, Success and Failure
IntermediateWhat happens between the click and the outcome is where forms are won or lost: no double submits, no lost input, and a result the user can actually see.
Overview
The submit handler is the moment the user commits, and it deserves more care than it usually gets. A button that stays enabled during the request creates duplicate records the moment someone double-clicks. A form that clears itself on failure loses work and reads as contempt. A success that produces no visible change makes people submit again. None of this is difficult — it is a checklist — but reviewers notice immediately when it is missing, and so do users.
The Pending State
Disable, label, and keep the layout stable.
<button type="submit" disabled={isSubmitting} aria-busy={isSubmitting}>
{isSubmitting ? 'Saving…' : 'Save changes'}
</button>
// Disabled alone is not enough feedback. A label change or spinner
// tells the user the click registered.
// Reserve the width so the button does not resize between the two
// labels, which reads as a jitter:
<span className="min-w-[8ch] inline-block text-center">…</span>
// Prevent the other paths to a double submit:
// - Enter in a text field submits the form too
// - a second click during the request
// Both are covered by disabling on isSubmitting, provided the flag
// is set before the await:
setSubmitting(true)
try { await save(values) } finally { setSubmitting(false) }
// For anything that creates a record, an idempotency key makes the
// duplicate harmless even if one slips through.Success
Say what happened, and move the user forward.
async function onSubmit(values) {
const created = await createProblem(values)
toast.success('Problem published') // it happened
queryClient.invalidateQueries({ queryKey: ['problems'] }) // the list updates
navigate(`/problems/${created.slug}`) // where next
}
// Pick the pattern from the context:
// creating something -> navigate to it
// editing in place -> stay, show "Saved", reset dirty state
// a modal -> close it, and update the list behind
// a multi-step flow -> advance to the next step
// reset(values) after an edit clears isDirty, which is what makes
// the unsaved-changes warning stop firing:
reset(created)
// A toast that vanishes in two seconds is not enough on its own for
// something important — reflect the change in the UI as well.Failure
The rules, and the one that matters most.
// 1. NEVER clear the form. The user's input is theirs.
// 2. Say which field, if the server told you which field.
// 3. Say what to do next.
// 4. Re-enable the button.
catch (err) {
if (err.status === 422) return applyFieldErrors(err)
if (err.status === 409) return setError('root', {
message: 'Someone else changed this while you were editing. Reload to see their version.',
})
if (err.status === 429) return setError('root', {
message: 'Too many attempts. Try again in a minute.',
})
setError('root', { message: 'We could not save that. Please try again.' })
reportError(err) // the detail goes to logging
}
// Move focus to the error so it is announced, not just displayed:
useEffect(() => {
if (errors.root) rootErrorRef.current?.focus()
}, [errors.root])
// Draft protection for long forms — a lost essay is unforgivable
useEffect(() => {
const id = setTimeout(() => localStorage.setItem(key, JSON.stringify(values)), 1000)
return () => clearTimeout(id)
}, [values])Key Points to Remember
- 1Disable and relabel the submit button while the request is in flight, setting the flag before the await
- 2Enter in a text field submits too — disabling on pending covers every path to a double submit
- 3On success, confirm what happened, refresh the affected data, and move the user forward
- 4On failure never clear the form, map errors to fields where possible, and re-enable the button
- 5Move focus to the error message so screen-reader users hear it, and autosave drafts for long forms
Interview Questions
Sign in to ask AriaHow do you prevent a form being submitted twice?
What should happen to the user's input when a submission fails?
How would you handle a 409 conflict on an edit form?
Ask Aria about Submission — Pending, Success and Failure
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.