New to FormEngine? It’s a React library that renders forms from JSON schemas — free, MIT-licensed.
Start here →
What You’ll Learn
In this tutorial, you’ll learn how to reset FormEngine forms by:
- Clearing all form fields
- Restoring forms to their initial values
- Resetting a form after successful submission
- Using key-based remounting to force a reset
- Programmatically resetting via
viewerRef
- Handling edge cases and common mistakes
Resetting forms is essential for multi-step workflows, clearing data after submission, and improving UX by giving users a way to start fresh.
The Simplest Approach: Key-Based Remounting
The most reliable way to reset a FormEngine form is to force React to remount the FormViewer component by changing its key prop. When a component’s key changes, React creates a new instance, and initialData is applied fresh.
import React, { useState } from 'react';
import { FormViewer } from '@react-form-builder/core';
import { view } from '@react-form-builder/components-rsuite';
const form = {
key: 'contactForm',
type: 'Screen',
children: [
{
key: 'name',
type: 'RsInput',
props: {
label: 'Your Name',
placeholder: 'Enter your name'
}
},
{
key: 'email',
type: 'RsInput',
props: {
label: 'Your Email',
placeholder: 'Enter your email'
}
},
{
key: 'message',
type: 'RsTextArea',
props: {
label: 'Message',
placeholder: 'Write your message here'
}
},
{
key: 'submitBtn',
type: 'RsButton',
props: {
label: 'Send Message'
}
}
]
};
export default function ContactForm() {
// Use a key to track form remounts
const [formKey, setFormKey] = useState(0);
const handleReset = () => {
// Incrementing the key forces React to remount the FormViewer
setFormKey(prevKey => prevKey + 1);
};
return (
<div>
<FormViewer
key={formKey}
view={view}
getForm={async () => form}
/>
<button onClick={handleReset} style={{ marginTop: '10px' }}>
Clear Form
</button>
</div>
);
}
When the user clicks Clear Form, the key changes, React unmounts and remounts the FormViewer, and all fields return to empty.
Why this works: React treats components with different key values as completely different instances. Remounting reinitializes all internal state, and initialData is applied fresh.
Reset to Initial Values
If your form has default values via initialData, remounting will restore those values:
import React, { useState } from 'react';
import { FormViewer } from '@react-form-builder/core';
import { view } from '@react-form-builder/components-rsuite';
const form = {
key: 'editForm',
type: 'Screen',
children: [
{
key: 'firstName',
type: 'RsInput',
props: { label: 'First Name' }
},
{
key: 'lastName',
type: 'RsInput',
props: { label: 'Last Name' }
},
{
key: 'role',
type: 'RsDropdown',
props: {
label: 'Role',
data: [
{ label: 'Admin', value: 'admin' },
{ label: 'User', value: 'user' }
]
}
},
{
key: 'saveBtn',
type: 'RsButton',
props: { label: 'Save Changes' }
}
]
};
export default function EditForm() {
const [formKey, setFormKey] = useState(0);
// These are the original/default values
const defaultValues = {
firstName: 'John',
lastName: 'Doe',
role: 'user'
};
const handleReset = () => {
// Reset back to the original values
setFormKey(prevKey => prevKey + 1);
};
return (
<div>
<FormViewer
key={formKey}
view={view}
getForm={async () => form}
initialData={defaultValues}
/>
<button onClick={handleReset} style={{ marginTop: '10px' }}>
Restore Original Values
</button>
</div>
);
}
Now when the user edits the form and clicks Restore Original Values, the form remounts with the original defaultValues.
Reset After Successful Submission
A common UX pattern is to clear the form after the user successfully submits it:
import React, { useState, useRef } from 'react';
import { FormViewer } from '@react-form-builder/core';
import { view } from '@react-form-builder/components-rsuite';
const form = {
key: 'surveyForm',
type: 'Screen',
children: [
{
key: 'q1',
type: 'RsInput',
props: { label: 'What is your name?' }
},
{
key: 'q2',
type: 'RsInput',
props: { label: 'What is your feedback?' }
},
{
key: 'submitBtn',
type: 'RsButton',
props: {
label: 'Submit Survey'
},
events: {
onClick: async (e) => {
// e.data contains the current form data
const formData = e.data;
try {
// Send to API
const response = await fetch('/api/survey', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
if (response.ok) {
alert('Survey submitted successfully!');
// Reset the form after successful submission
e.form.reset?.();
} else {
alert('Submission failed. Please try again.');
}
} catch (error) {
alert('Error: ' + error.message);
}
}
}
}
]
};
export default function SurveyForm() {
const [formKey, setFormKey] = useState(0);
const formRef = useRef(null);
// Alternative: if e.form.reset() is not available,
// you can trigger the remount from the parent
const resetForm = () => {
setFormKey(prevKey => prevKey + 1);
};
return (
<FormViewer
key={formKey}
view={view}
getForm={async () => form}
viewerRef={formRef}
/>
);
}
In this pattern, the form’s submit button has an onClick action that sends data to the API. If the submission succeeds, the form resets. You can either call e.form.reset?.() (if available in your version) or manage the reset from the parent component.
If you don’t need to restore to initial values and just want to clear the form completely, remounting without initialData does the trick:
import React, { useState } from 'react';
import { FormViewer } from '@react-form-builder/core';
import { view } from '@react-form-builder/components-rsuite';
const form = {
key: 'quickForm',
type: 'Screen',
children: [
{
key: 'input1',
type: 'RsInput',
props: { label: 'Field 1' }
},
{
key: 'input2',
type: 'RsInput',
props: { label: 'Field 2' }
},
{
key: 'clearBtn',
type: 'RsButton',
props: { label: 'Clear All' }
}
]
};
export default function QuickForm() {
const [formKey, setFormKey] = useState(0);
const handleClear = () => {
setFormKey(prevKey => prevKey + 1);
};
return (
<div>
<FormViewer
key={formKey}
view={view}
getForm={async () => form}
// No initialData — form starts empty
/>
<button onClick={handleClear}>
Clear Form
</button>
</div>
);
}
All fields render empty, and the button clears them completely.
Programmatic Reset via viewerRef
If your version of FormEngine exposes methods on viewerRef.current, you can reset programmatically without remounting:
import React, { useRef } from 'react';
import { FormViewer } from '@react-form-builder/core';
import { view } from '@react-form-builder/components-rsuite';
const form = {
key: 'demoForm',
type: 'Screen',
children: [
{
key: 'username',
type: 'RsInput',
props: { label: 'Username' }
},
{
key: 'password',
type: 'RsInput',
props: { label: 'Password', type: 'password' }
}
]
};
export default function LoginForm() {
const formRef = useRef(null);
const handleReset = () => {
if (formRef.current && typeof formRef.current.reset === 'function') {
// If reset method exists, use it
formRef.current.reset();
} else if (formRef.current) {
// Alternative: manually clear form data
formRef.current.formData = {
username: '',
password: ''
};
}
};
return (
<div>
<FormViewer
view={view}
getForm={async () => form}
viewerRef={formRef}
/>
<button onClick={handleReset}>
Reset Form
</button>
</div>
);
}
Note: The availability of reset() method depends on your FormEngine version. Check your documentation or the viewerRef.current object to see what methods are available. If not available, stick with the key-based remounting approach.
Track form changes while allowing reset. When you reset, the form state clears:
import React, { useState, useRef } from 'react';
import { FormViewer } from '@react-form-builder/core';
import { view } from '@react-form-builder/components-rsuite';
const form = {
key: 'trackForm',
type: 'Screen',
children: [
{
key: 'name',
type: 'RsInput',
props: { label: 'Name' }
},
{
key: 'email',
type: 'RsInput',
props: { label: 'Email' }
}
]
};
export default function TrackAndResetForm() {
const [formKey, setFormKey] = useState(0);
const [currentData, setCurrentData] = useState({});
const formRef = useRef(null);
const handleFormDataChange = ({ data, errors }) => {
setCurrentData(data);
};
const handleReset = () => {
setCurrentData({});
setFormKey(prevKey => prevKey + 1);
};
return (
<div>
<FormViewer
key={formKey}
view={view}
getForm={async () => form}
onFormDataChange={handleFormDataChange}
viewerRef={formRef}
/>
<div style={{ marginTop: '10px' }}>
<p>Current data: {JSON.stringify(currentData)}</p>
<button onClick={handleReset}>Clear Form</button>
</div>
</div>
);
}
Now you can monitor form changes in real-time and reset both the form state and your tracking state together.
Common Mistakes
1. Forgetting to Change the Key
If you try to reset without changing the key, nothing happens because React sees the same component and doesn’t remount:
// ❌ Wrong — key never changes
const handleReset = () => {
// Do something, but don't change key
console.log('Reset');
};
return <FormViewer key={0} ... />; // Key is always 0
// ✅ Correct
const [formKey, setFormKey] = useState(0);
const handleReset = () => {
setFormKey(prevKey => prevKey + 1);
};
return <FormViewer key={formKey} ... />;
2. Assuming viewerRef.current Has a reset() Method
Different versions of FormEngine expose different APIs. Don’t assume reset() exists without checking:
// ❌ May fail if reset() doesn't exist
formRef.current.reset();
// ✅ Check first
if (formRef.current && typeof formRef.current.reset === 'function') {
formRef.current.reset();
} else {
// Fallback to key-based approach
setFormKey(prevKey => prevKey + 1);
}
3. Stale Closure in Event Handlers
If you’re resetting inside an event handler, make sure you’re using the latest state:
// ❌ Stale closure — formKey never changes inside the handler
const handleReset = () => {
const newKey = formKey + 1; // Captures old formKey
setFormKey(newKey);
};
// ✅ Use setState with callback
const handleReset = () => {
setFormKey(prevKey => prevKey + 1); // Correct
};
4. Resetting Too Frequently
Remounting the form every render or too frequently can cause performance issues and loss of focus states. Reset only when explicitly requested by the user:
// ❌ Too aggressive — resets on every prop change
useEffect(() => {
setFormKey(prevKey => prevKey + 1);
}, [someProp]); // Remounts form constantly
// ✅ Reset only on user action
const handleReset = () => {
setFormKey(prevKey => prevKey + 1);
};
5. Not Clearing Associated State
If you’re tracking form data separately (e.g., in parent state), reset that too:
// ❌ Form resets but parent state doesn't
const handleReset = () => {
setFormKey(prevKey => prevKey + 1);
// currentData state is NOT cleared
};
// ✅ Reset both
const handleReset = () => {
setCurrentData({});
setFormKey(prevKey => prevKey + 1);
};
When to Use Each Approach
| Scenario | Approach | Why |
|---|
| Clear form, restore empty | Key-based remount | Simple, reliable, no API assumptions |
| Restore to initial/default values | Key-based remount with initialData | Works with any form, any version |
| Clear after successful submit | Key-based remount in submit handler | Guarantees clean state for next submission |
| Performance-critical (avoid remount) | viewerRef.current.reset() or manual data clear | If available, avoids full remount overhead |
| Track changes while resetting | Key + state management | Allows both monitoring and clearing |
Next Steps
Now that you can reset forms, explore these related topics:
Last modified on April 16, 2026