@form-guardian/react
v1.0.5
Published
React hooks for form autosave functionality. Provides useFormAutosave hook for easy integration with React forms and useDraftStatus hook for real-time draft status monitoring. Works seamlessly with React Hook Form, Formik, and plain React forms.
Maintainers
Readme
@form-guardian/react
React hooks for form autosave functionality. Provides useFormAutosave hook for easy integration with React forms and useDraftStatus hook for real-time draft status monitoring. Works seamlessly with React Hook Form, Formik, and plain React forms.
⚠️ Warning:
useFormAutosaveis currently unstable and may have issues with certain use cases. For production applications, consider usingattachFormAutosavefrom@form-guardian/domdirectly, which is more stable and reliable.
Overview
@form-guardian/react provides React-specific hooks that wrap the universal @form-guardian/dom functionality. It offers a more React-idiomatic API with hooks, automatic cleanup, and type-safe integration with React form libraries.
Installation
npm install @form-guardian/reactPeer Dependencies:
- React >= 16.8.0 (hooks support required)
Features
- ✅ React Hooks API -
useFormAutosaveanduseDraftStatushooks - ✅ TypeScript Support - Full type inference for form data
- ✅ Automatic Cleanup - Handles cleanup on unmount
- ✅ React Hook Form Integration - Works seamlessly with RHF
- ✅ Formik Integration - Compatible with Formik forms
- ✅ Plain React Forms - Works with controlled components
- ✅ Draft Status Monitoring - Real-time draft status with
useDraftStatus
Quick Start
Basic Usage
import { useFormAutosave } from '@form-guardian/react';
function ContactForm() {
const { formRef, hasDraft, restoreDraft, clearDraft } = useFormAutosave('contact-form', {
autoRestore: true,
onAfterSave: (values) => {
console.log('Draft saved:', values);
}
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// ... submit logic
await clearDraft(); // Clear draft on successful submit
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input name="name" />
<input name="email" />
<button type="submit">Submit</button>
{hasDraft && (
<button type="button" onClick={restoreDraft}>
Restore Draft
</button>
)}
</form>
);
}With React Hook Form
import { useForm } from 'react-hook-form';
import { useFormAutosave } from '@form-guardian/react';
interface FormData {
name: string;
email: string;
message: string;
}
function MyForm() {
const { register, handleSubmit, setValue } = useForm<FormData>();
const { formRef, restoreValues } = useFormAutosave<FormData>('my-form', {
autoRestore: false, // We'll restore manually
});
// Restore draft values into React Hook Form
useEffect(() => {
restoreValues(setValue);
}, [restoreValues, setValue]);
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} />
<input {...register('email')} />
<textarea {...register('message')} />
<button type="submit">Submit</button>
</form>
);
}With Draft Status
import { useFormAutosave, useDraftStatus } from '@form-guardian/react';
function FormWithStatus() {
const { formRef } = useFormAutosave('my-form');
const { hasDraft, updatedAt, isExpired, isChecking } = useDraftStatus('my-form');
return (
<div>
<form ref={formRef}>
{/* form fields */}
</form>
{hasDraft && updatedAt && (
<div>
<p>Draft saved: {new Date(updatedAt).toLocaleString()}</p>
{isExpired && <p>⚠️ Draft has expired</p>}
{isChecking && <p>Checking draft...</p>}
</div>
)}
</div>
);
}API
useFormAutosave<T>(formId, options?)
React hook for form autosave functionality.
⚠️ Warning: This hook is currently unstable. For production use, consider using
attachFormAutosavefrom@form-guardian/domdirectly.
Parameters
formId- Unique identifier for the formoptions- Configuration options (same asattachFormAutosave)
Returns
formRef- Ref to attach to form elementhasDraft- Boolean indicating if draft existsdraftTimestamp- Timestamp of draft (ornull)restoreDraft()- Function to manually restore draftgetDraftValues()- Get draft values as objectrestoreValues(setValue, getDefaultValues?)- Restore values for controlled componentsclearDraft()- Clear the draftsaveValues(values?)- Manually save values
useDraftStatus<T>(formId, options?)
Lightweight hook for checking draft status without touching the DOM.
Parameters
formId- Unique identifier for the formoptions- Configuration options:includeOrigin- Include origin in draft ID (default:true)storagePrefix- Storage prefix (default:'fg')ttl- Time to live for drafts
Returns
hasDraft- Boolean indicating if draft existsisExpired- Boolean indicating if draft has expiredupdatedAt- Timestamp of last update (ornull)isChecking- Boolean indicating if status is being checkedrefresh()- Function to manually refresh statusclear()- Function to clear the draft
Examples
With Analytics Events
const { formRef } = useFormAutosave<FormData>('checkout-form', {
onBeforeSave: async (values) => {
analytics.track('draft_save_started');
},
onAfterSave: async (values) => {
analytics.track('draft_saved', { fieldCount: Object.keys(values).length });
},
onBeforeRestore: async (values) => {
analytics.track('draft_restore_started');
},
onAfterRestore: async (values) => {
analytics.track('draft_restored');
},
onDraftExpired: async (draftId) => {
analytics.track('draft_expired', { draftId });
}
});With Batching
const { formRef } = useFormAutosave('large-form', {
batchSaveInterval: 5000, // Save every 5 seconds
onAfterSave: (values) => {
console.log('Batch saved');
}
});TypeScript with Custom Types
interface ContactFormData {
name: string;
email: string;
phone: string;
message: string;
}
function ContactForm() {
const { formRef, getDraftValues } = useFormAutosave<ContactFormData>('contact-form', {
onAfterSave: (values) => {
// values is typed as ContactFormData
console.log(values.name); // TypeScript knows this exists
}
});
const handleLoadDraft = async () => {
const draft = await getDraftValues();
if (draft) {
// draft is typed as ContactFormData | null
console.log(draft.email);
}
};
return <form ref={formRef}>...</form>;
}Draft Status Banner
function DraftBanner({ formId }: { formId: string }) {
const { hasDraft, updatedAt, isExpired, clear } = useDraftStatus(formId, {
ttl: { days: 7 }
});
if (!hasDraft) return null;
return (
<div className="draft-banner">
{isExpired ? (
<p>⚠️ Your draft has expired.</p>
) : (
<p>📝 You have a draft from {new Date(updatedAt!).toLocaleString()}</p>
)}
<button onClick={() => clear()}>Discard</button>
</div>
);
}When to Use
Use @form-guardian/react if you:
- Are building a React application
- Want a React-idiomatic API with hooks
- Need type-safe integration with React form libraries
- Want automatic cleanup and lifecycle management
- Need real-time draft status monitoring
For non-React applications or custom integrations, use @form-guardian/dom instead.
