Loading...
Please waitLoading...
Please waitpnpm dlx uselab@latest add use-form-autosave
import * as React from "react"
import { useFormAutosave } from "@/hooks/use-form-autosave"
interface ApplicationForm {
applicant: { name: string; email: string }
summary: string
sections: Array<{ id: string; text: string }>
}
const INITIAL_VALUES: ApplicationForm = {
applicant: { name: "", email: "" },
summary: "",
sections: [],
}
export function ApplicationFormPage() {
const [values, setValues] = React.useState<ApplicationForm>(INITIAL_VALUES)
const autosave = useFormAutosave("application-form", values, {
debounceMs: 1500,
})
const handleRestore = () => {
const draft = autosave.restore()
if (draft) setValues(draft)
}
return (
<form className="space-y-4">
{/* form fields */}
<footer className="flex items-center gap-3 text-sm text-muted-foreground">
<span>
{autosave.isSaving
? "Saving…"
: autosave.savedAt
? `Saved at ${new Date(autosave.savedAt).toLocaleTimeString()}`
: "Draft not saved yet"}
</span>
<button
type="button"
onClick={handleRestore}
disabled={!autosave.hasDraft}
>
Restore draft
</button>
<button
type="button"
onClick={autosave.clear}
disabled={!autosave.hasDraft}
>
Clear draft
</button>
</footer>
</form>
)
}| Name | Type | Description | Default Value | Optional |
|---|---|---|---|---|
name | string | Identifier used for the storage key (usekit:form-autosave:name). | — | No |
values | T | Any serializable form data (objects, arrays, deeply nested). | — | No |
options.debounceMs | number | Delay (ms) before persisting after changes. | 1200 | Yes |
options.storage | Storage | Custom storage (e.g., sessionStorage). | localStorage | Yes |
options.enabled | boolean | Toggle autosave logic. | true | Yes |
options.flushOnUnload | boolean | Flush pending drafts automatically when the page hides or unloads. | false | Yes |
| Name | Type | Description |
|---|---|---|
savedAt | number | null | Timestamp of the last persisted draft. |
isSaving | boolean | true while a debounce cycle is writing to storage. |
hasDraft | boolean | true if a draft exists in storage. |
hasConflict | boolean | true when another session saved newer data. |
conflict | AutosaveConflict<T> | null | The conflicting draft payload and metadata. |
restore() | () => T | null | Returns the stored values so you can hydrate your form. |
clear() | () => void | Removes the draft and resets metadata. |
flush() | () => void | Persists the current values immediately, bypassing debounce. |
sessionId. When storage is modified from a different session (another tab, device, etc.), the hook surfaces hasConflict and the conflicting values.conflict payload to preview or merge incoming drafts. Calling restore() resolves the conflict and hydrates your form with the remote values.storage event listener keeps tabs in sync, even without manual refresh.JSON.stringify.storage namespace (name) per form instance (e.g., "application-form" or "editor:doc-123").storage option with a custom adapter that proxies to IndexedDB or localforage.flush() whenever you need an immediate save (e.g., before submitting or navigating away), or enable flushOnUnload to automate that behavior.