Atlas Forms
Draft Auto-Save
Atlas Forms automatically saves form state to local storage as the user types, with a 500ms debounce. When the user returns to the form after a navigation, crash, or session close, they are offered the option to restore their in-progress data from the draft. The draft is cleared on successful submission.
How It Works
- On every field change, the
FormEnginefires achangeevent. - The draft system listens to this event with a 500ms debounce.
- After 500ms of inactivity, it serialises the current form values to JSON.
- The JSON is saved via
StorageManagerwhich uses a fallback chain: localStorage → IndexedDB → memory. - The storage key is
atlas-forms-draft:{draftKey}wheredraftKeydefaults to the form's ID. - On form mount, if a draft exists for this key, the player shows a "Restore draft?" prompt.
- On successful submit, the draft is deleted from storage.
StorageManager Fallback Chain
// packages/storage-js/src/StorageManager.ts
class StorageManager {
async save(key: string, value: any): Promise<void> {
const json = JSON.stringify(value);
try {
localStorage.setItem(key, json);
} catch (localStorageError) {
// localStorage full or unavailable — try IndexedDB
try {
await this.idb.put(key, json);
} catch (idbError) {
// IndexedDB unavailable — fall back to in-memory map
this.memoryStore.set(key, json);
}
}
}
}
Draft Restore Prompt
When a draft is found on form mount, FormPlayerPage and FormRenderer show a non-intrusive banner at the top of the form:
// Conceptual prompt behaviour (handled internally)
"You have unsaved changes from [timestamp]. Would you like to restore them?"
[Restore Draft] [Start Fresh]
// "Restore Draft" → initialValues are replaced with the saved draft values
// "Start Fresh" → draft is deleted; form initialises with the provided initialValues
Configuring Draft Behaviour
// Disable draft saving entirely
<FormRenderer draftEnabled={false} />
// Custom draft key — use when multiple form instances exist on the same page
<FormRenderer
draftKey={`onboarding-${userId}-step-${stepNumber}`}
/>
// For single-submission forms where draft restore would be confusing,
// clear any existing draft on mount:
useEffect(() => {
storageManager.delete(`atlas-forms-draft:${formId}`);
}, [formId]);
Draft Storage Size
Draft data is the serialised form values — typically a few KB for most forms. Forms with large binary fields (file uploads, json-editor with large objects) should use custom draftKey strategies and may need to exclude large fields from draft saving:
// Exclude file upload fields from draft by using a custom serialiser
// (advanced — requires a custom FormStateProvider setup)
const draftSerializer = (values: Record<string, any>) => {
const { 'file-upload-field': _, ...saveable } = values;
return saveable;
};
Draft Lifecycle Events
| Event | When Fired |
|---|---|
draft:saved | After each debounced auto-save completes |
draft:restored | When the user accepts the restore prompt |
draft:discarded | When the user declines the restore prompt |
draft:cleared | After successful form submission |