Runnable lesson · press Try it on the examples to edit and run them here.
After a successful submit or when the user presses Cancel, you want the form back in its starting condition. How you do it depends on how the form stores its values.
Controlled form: set state back
Keep the starting values in one constant and reuse it:
const initialValues = { name: "", comment: "" };
function App() {
const [values, setValues] = React.useState(initialValues);
function handleChange(e) {
setValues({ ...values, [e.target.name]: e.target.value });
}
return (
<form onSubmit={(e) => e.preventDefault()}>
<h3>Feedback</h3>
<input name="name" aria-label="Name" value={values.name} onChange={handleChange} />
<input name="comment" aria-label="Comment" value={values.comment} onChange={handleChange} />
<button type="button" onClick={() => setValues(initialValues)}>Reset</button>
</form>
);
}
Because the state is the source of truth, the inputs update at once.
Reset everything with key
If a form has more state than just its fields (touched flags, errors, status), resetting each piece is easy to forget. Put the form in a component and change its key to throw all its state away:
function NoteForm() {
const [note, setNote] = React.useState("");
const [touched, setTouched] = React.useState(false);
return (
<div>
<input aria-label="Note" value={note} onChange={(e) => { setNote(e.target.value); setTouched(true); }} />
<p>{touched ? "Edited" : "Untouched"}</p>
</div>
);
}
function App() {
const [formKey, setFormKey] = React.useState(0);
return (
<div>
<NoteForm key={formKey} />
<button onClick={() => setFormKey(formKey + 1)}>Start over</button>
<p>Reset count: {formKey}</p>
</div>
);
}
A new key means a new component instance, with brand-new state. This is the most reliable way to reset a complex form.
Uncontrolled form: the native reset()
For a form read with FormData, the browser can reset its own fields:
function App() {
const formRef = React.useRef(null);
return (
<form ref={formRef} onSubmit={(e) => e.preventDefault()}>
<p>Uncontrolled</p>
<input aria-label="Name" name="name" defaultValue="" />
<button type="button" onClick={() => formRef.current.reset()}>Clear</button>
</form>
);
}
form.reset() restores each field to its defaultValue. (A <button type="reset"> does the same without code, but it is easy to click by mistake, so be careful where you put it.)
Reset to saved values, not to empty
When you are editing existing data, Cancel should go back to what was saved, not to blank:
function App() {
const saved = { displayName: "Ada" };
const [draft, setDraft] = React.useState(saved);
const dirty = draft.displayName !== saved.displayName; // derived: has the user changed anything?
return (
<div>
<p>Editing profile</p>
<input aria-label="Display name" value={draft.displayName} onChange={(e) => setDraft({ displayName: e.target.value })} />
<button disabled={!dirty} onClick={() => setDraft(saved)}>Cancel</button>
{dirty && <p role="status">You have unsaved changes</p>}
</div>
);
}
dirty is derived by comparing the draft with the saved data. It can power an “unsaved changes” warning, which you can combine with the browser’s beforeunload event.
React 19 form actions reset uncontrolled fields
When you use a form action (see Form Actions), React automatically resets uncontrolled fields after the action succeeds. You can also call requestFormReset(form) yourself.
Common mistakes
- Resetting values but forgetting touched flags and error messages: use the
keyapproach. - Using one shared object for initial values and mutating it. Always create new objects (see Avoiding State Mutation).
- Resetting while a request is still running. Disable the button or ignore the late response.
Key points
- Controlled forms reset by setting the state back to the initial values.
- Changing a
keyresets all of a component’s state in one move. - Uncontrolled forms can use
form.reset(); edit forms should reset to the saved data.