Skip to main content

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

  1. Fill form → Close tab → Reopen

    • ✅ Draft should be restored
  2. Fill form → Refresh page

    • ✅ Draft should be restored
  3. Fill form → Submit → Check storage

    • ✅ Draft should be cleared
  4. 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');
},
});

Next Steps