Prevent Form Data Loss
Form data loss is one of the most frustrating user experiences. This guide shows you how to prevent it using Form Guardian and best practices.
Common Scenarios Where Data is Lost
1. Accidental Tab Close
// User fills out long form
// Accidentally closes browser tab
// ❌ All data lost
Solution: Form Guardian automatically saves drafts, so data is preserved even if the tab is closed.
2. Page Refresh
// User is filling form
// Page refreshes (network issue, browser crash, etc.)
// ❌ All data lost
Solution: Form Guardian restores drafts automatically on page load.
3. Navigation Away
// User clicks back button or navigates to another page
// ❌ Form data lost
Solution: Drafts persist across navigation and can be restored when user returns.
4. Browser Crash
// Browser crashes while user is typing
// ❌ All unsaved data lost
Solution: IndexedDB persists data even through browser crashes.
Complete Solution with Form Guardian
Basic Setup
import { useFormAutosave } from '@form-guardian/react';
function MyForm() {
const { formRef, clearDraft } = useFormAutosave('my-form', {
autoRestore: true, // Restore draft automatically
debounceMs: 500, // Save 500ms after user stops typing
ttl: { days: 7 }, // Keep draft for 7 days
});
const handleSubmit = async (e) => {
e.preventDefault();
// Submit logic
await clearDraft(); // Clear after successful submission
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
{/* Form fields */}
</form>
);
}
Best Practices
1. Set Appropriate TTL
// Short-lived forms (contact, feedback)
ttl: { hours: 24 }
// Long forms (applications, surveys)
ttl: { days: 7 }
// Very long forms (drafts, compositions)
ttl: { days: 30 }
2. Clear Drafts After Submission
const handleSubmit = async (data) => {
try {
await submitForm(data);
await clearDraft(); // ✅ Clear after success
} catch (error) {
// Keep draft if submission fails
console.error('Submission failed, draft preserved');
}
};
3. Show Draft Status to Users
function MyForm() {
const { formRef, hasDraft, draftTimestamp, restoreDraft, clearDraft } =
useFormAutosave('my-form', { autoRestore: true });
return (
<form ref={formRef}>
{hasDraft && (
<div className="draft-notice">
<p>You have a saved draft from {new Date(draftTimestamp).toLocaleString()}</p>
<button type="button" onClick={restoreDraft}>
Restore Draft
</button>
<button type="button" onClick={clearDraft}>
Discard Draft
</button>
</div>
)}
{/* Form fields */}
</form>
);
}
4. Handle Multiple Forms
// Each form needs unique ID
const contactForm = useFormAutosave('contact-form', { autoRestore: true });
const newsletterForm = useFormAutosave('newsletter-form', { autoRestore: true });
const feedbackForm = useFormAutosave('feedback-form', { autoRestore: true });
5. Exclude Sensitive Fields
const { formRef } = useFormAutosave('my-form', {
autoRestore: true,
blacklist: [
'input[name="password"]',
'input[name="ssn"]',
'input[name="credit-card"]',
],
});
Real-World Examples
E-commerce Checkout Form
function CheckoutForm() {
const { formRef, hasDraft, clearDraft } = useFormAutosave('checkout', {
autoRestore: true,
ttl: { hours: 2 }, // Short TTL for checkout
blacklist: ['input[name="cvv"]', 'input[name="card-number"]'],
});
const handleSubmit = async (data) => {
await processPayment(data);
await clearDraft(); // Clear after successful payment
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input name="shipping-address" />
<input name="billing-address" />
{/* Payment fields automatically excluded */}
<button type="submit">Complete Purchase</button>
</form>
);
}
Long Application Form
function ApplicationForm() {
const { formRef, hasDraft, draftTimestamp, restoreDraft } =
useFormAutosave('application', {
autoRestore: false, // Let user choose to restore
ttl: { days: 30 }, // Long TTL for applications
});
useEffect(() => {
if (hasDraft) {
const shouldRestore = confirm(
`You have a saved draft from ${new Date(draftTimestamp).toLocaleString()}. Restore it?`
);
if (shouldRestore) {
restoreDraft();
}
}
}, []);
return (
<form ref={formRef}>
{/* Long form with many fields */}
</form>
);
}
Multi-Step Form
function MultiStepForm() {
const [step, setStep] = useState(1);
const { formRef, clearDraft } = useFormAutosave('multi-step-form', {
autoRestore: true,
ttl: { days: 7 },
});
const handleComplete = async () => {
await submitAllSteps();
await clearDraft();
};
return (
<form ref={formRef}>
{step === 1 && <Step1 />}
{step === 2 && <Step2 />}
{step === 3 && <Step3 />}
<button onClick={handleComplete}>Complete</button>
</form>
);
}
Testing Your Implementation
Test Scenarios
-
Fill form → Close tab → Reopen
- ✅ Draft should be restored
-
Fill form → Refresh page
- ✅ Draft should be restored
-
Fill form → Submit → Check storage
- ✅ Draft should be cleared
-
Fill form → Wait for TTL → Check storage
- ✅ Draft should be expired
Monitoring & Analytics
Track draft usage:
const { formRef, hasDraft } = useFormAutosave('my-form', {
onSave: () => {
analytics.track('draft_saved');
},
onRestore: () => {
analytics.track('draft_restored');
},
});