@form-guardian/react
React hooks and components for form autosave. Built on top of @form-guardian/dom.
Installation
npm install @form-guardian/react
Peer Dependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
useFormAutosave(formId, options)
Main React hook for form autosave functionality.
useFormAutosave is currently unstable and may have issues with certain use cases. For production applications, consider using attachFormAutosave from @form-guardian/dom directly, which is more stable and reliable.
Import:
import { useFormAutosave } from '@form-guardian/react';
Parameters:
formId(string, required) - Unique identifier for the formoptions(FormAutosaveOptions, optional) - Configuration options
Returns: UseFormAutosaveReturn
Example:
import { useFormAutosave } from '@form-guardian/react';
function MyForm() {
const {
formRef,
hasDraft,
draftTimestamp,
restoreDraft,
clearDraft,
getDraftValues,
restoreValues,
saveValues,
} = useFormAutosave('my-form', {
autoRestore: true,
debounceMs: 300,
batchSaveInterval: 5000, // Save changes in batches every 5 seconds
onBeforeSave: async (values) => {
console.log('About to save:', values);
},
onAfterSave: async (values) => {
console.log('Saved successfully:', values);
},
});
return (
<form ref={formRef}>
{hasDraft && (
<div className="draft-banner">
Draft saved at: {new Date(draftTimestamp).toLocaleString()}
</div>
)}
{/* form fields */}
</form>
);
}
UseFormAutosaveReturn
Object returned by useFormAutosave() hook.
Properties
formRef: RefObject<HTMLFormElement>
Ref to attach to your form element. Required.
<form ref={formRef}>...</form>
hasDraft: boolean
Whether a draft exists for this form.
{hasDraft && <div>You have unsaved changes</div>}
draftTimestamp: number | null
Timestamp when the draft was last updated, or null if no draft exists.
{draftTimestamp && (
<div>Last saved: {new Date(draftTimestamp).toLocaleString()}</div>
)}
Methods
restoreDraft(): Promise<void>
Manually restore draft to form. Usually not needed if autoRestore: true.
<button onClick={() => restoreDraft()}>Restore Draft</button>
clearDraft(): Promise<void>
Clear draft from storage.
const handleSubmit = async (e) => {
e.preventDefault();
await clearDraft();
// Submit form...
};
getDraftValues(): Promise<T | null>
Get draft values as an object without restoring to form.
const values = await getDraftValues();
console.log(values); // { name: 'John', email: 'john@example.com' }
restoreValues(setValue, getValues): Promise<void>
Restore values for controlled components (React Hook Form, Formik, etc.).
// React Hook Form example
const { setValue, getValues } = useForm();
const handleRestore = async () => {
await restoreValues(setValue, getValues);
};
saveValues(): Promise<void>
Manually save current form values. Useful for controlled forms.
<button onClick={() => saveValues()}>Save Draft</button>
useDraftStatus(formId, options)
Lightweight hook to get draft status without DOM interaction. Perfect for showing draft indicators outside the form component.
Import:
import { useDraftStatus } from '@form-guardian/react';
Parameters:
formId(string, required) - Unique identifier for the formoptions(UseDraftStatusOptions, optional) - Configuration options
Returns: UseDraftStatusResult
Example:
import { useDraftStatus } from '@form-guardian/react';
function DraftIndicator() {
const {
hasDraft,
updatedAt,
isExpired,
isChecking,
refresh,
clear
} = useDraftStatus('my-form', {
ttl: { days: 7 },
});
if (isChecking) return <div>Checking...</div>;
if (!hasDraft) return null;
if (isExpired) return <div>Draft expired</div>;
return (
<div>
Draft saved at: {new Date(updatedAt).toLocaleString()}
<button onClick={clear}>Clear</button>
<button onClick={refresh}>Refresh</button>
</div>
);
}
UseDraftStatusResult
Object returned by useDraftStatus() hook.
Properties
hasDraft: boolean
Whether a draft exists.
isExpired: boolean
Whether the draft has expired (based on TTL).
updatedAt: number | null
Timestamp when draft was last updated.
isChecking: boolean
Whether the status is currently being checked.
Methods
refresh(): Promise<void>
Manually refresh draft status from storage.
clear(): Promise<void>
Clear draft from storage.
Use Cases
Uncontrolled Form
import { useFormAutosave } from '@form-guardian/react';
function ContactForm() {
const { formRef, hasDraft, clearDraft } = useFormAutosave('contact-form', {
autoRestore: true,
debounceMs: 500,
});
const handleSubmit = async (e) => {
e.preventDefault();
await clearDraft();
// Submit form...
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
{hasDraft && <div className="alert">Unsaved changes detected</div>}
<input name="name" placeholder="Name" />
<input name="email" type="email" placeholder="Email" />
<button type="submit">Submit</button>
</form>
);
}
React Hook Form (Controlled)
import { useForm } from 'react-hook-form';
import { useFormAutosave } from '@form-guardian/react';
function MyForm() {
const { register, handleSubmit, setValue, getValues } = useForm();
const { formRef, restoreValues, clearDraft, saveValues } = useFormAutosave(
'my-form',
{
autoRestore: false, // Manual restore for controlled forms
debounceMs: 300,
}
);
// Restore draft on mount
useEffect(() => {
restoreValues(setValue, getValues);
}, []);
// Auto-save on field changes
const { watch } = useForm();
useEffect(() => {
const subscription = watch(() => {
saveValues();
});
return () => subscription.unsubscribe();
}, [watch]);
const onSubmit = async (data) => {
await clearDraft();
// Submit data...
};
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} />
<input {...register('email')} />
<button type="submit">Submit</button>
</form>
);
}
Draft Status Indicator (Separate Component)
import { useDraftStatus } from '@form-guardian/react';
function Header() {
const { hasDraft, updatedAt } = useDraftStatus('contact-form');
return (
<header>
<h1>My App</h1>
{hasDraft && (
<div className="draft-badge">
📝 Unsaved draft from {new Date(updatedAt).toLocaleTimeString()}
</div>
)}
</header>
);
}