Skip to main content

@form-guardian/dom

Universal DOM-based form autosave. Works with any framework or vanilla HTML forms.

Installation

npm install @form-guardian/dom

attachFormAutosave(options)

Main function to attach autosave functionality to a form.

Import:

import { attachFormAutosave } from '@form-guardian/dom';

Parameters:

  • formId (string, required) - Unique identifier for the form
  • root (HTMLFormElement | HTMLElement, required) - Root element (form or container)
  • autoRestore (boolean, optional) - Auto-restore on mount (default: false)
  • debounceMs (number, optional) - Debounce delay in milliseconds (default: 500)
  • ttl (object | number, optional) - Time to live for drafts
  • blacklist (string[], optional) - CSS selectors for fields to exclude
  • whitelist (string[], optional) - CSS selectors for fields to include
  • fieldSelector (string, optional) - Selector for fields to track (default: 'input, textarea, [contenteditable="true"]')
  • excludeSelector (string, optional) - Selector for fields to exclude (default: 'input[type="submit"], input[type="button"], input[type="reset"]')
  • getFieldKey (function, optional) - Function to get field key
  • onSave (function, optional) - Callback when draft is saved (legacy)
  • onRestore (function, optional) - Callback when draft is restored (legacy)
  • onBeforeSave (function, optional) - Callback before draft is saved
  • onAfterSave (function, optional) - Callback after draft is successfully saved
  • onBeforeRestore (function, optional) - Callback before draft is restored
  • onAfterRestore (function, optional) - Callback after draft is successfully restored
  • onDraftExpired (function, optional) - Callback when draft expires due to TTL
  • batchSaveInterval (number, optional) - Batch save interval in milliseconds (0 = disabled, default: disabled)
  • includeOrigin (boolean, optional) - Include origin in draft ID (default: true)
  • storagePrefix (string, optional) - Storage key prefix (default: 'fg')

Returns: FormAutosaveHandle

Example:

const handle = attachFormAutosave({
formId: 'my-form',
root: document.getElementById('my-form'),
autoRestore: true,
debounceMs: 300,
onBeforeSave: async (values) => {
console.log('Saving draft...', values);
},
onAfterSave: async (values) => {
console.log('Draft saved successfully!');
},
});

// Methods available on handle:
handle.restore(); // Restore draft
handle.clear(); // Clear draft
handle.destroy(); // Remove event listeners
handle.hasDraft(); // Check if draft exists
handle.getCurrentValues(); // Get current form values

FormAutosaveHandle

Object returned by attachFormAutosave().

Methods

restore(): Promise<void>

Restore draft values to the form.

await handle.restore();

clear(): Promise<void>

Clear draft from storage.

await handle.clear();

destroy(): void

Remove all event listeners and clean up. Call this when unmounting.

handle.destroy();

getCurrentValues(): Promise<T>

Get current form values as an object.

const values = await handle.getCurrentValues();
console.log(values); // { name: 'John', email: 'john@example.com' }

hasDraft(): Promise<boolean>

Check if a draft exists for this form.

const exists = await handle.hasDraft();
if (exists) {
console.log('Draft found!');
}

getDraftMeta(): Promise<DraftMeta | null>

Get draft metadata (timestamp, formId, etc.) without loading values.

const meta = await handle.getDraftMeta();
if (meta) {
console.log('Draft last updated:', new Date(meta.updatedAt));
}

Use Cases

Vanilla JavaScript

import { attachFormAutosave } from '@form-guardian/dom';

const form = document.getElementById('contact-form');
const autosave = attachFormAutosave({
formId: 'contact-form',
root: form,
autoRestore: true,
debounceMs: 500,
});

form.addEventListener('submit', async (e) => {
e.preventDefault();
// Clear draft after successful submission
await autosave.clear();
// Submit form data...
});

Vue 3

<script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { attachFormAutosave } from '@form-guardian/dom';

const formRef = ref(null);
let autosave = null;

onMounted(() => {
autosave = attachFormAutosave({
formId: 'my-form',
root: formRef.value,
autoRestore: true,
});
});

onBeforeUnmount(() => {
if (autosave) {
autosave.destroy();
}
});

const handleSubmit = async () => {
await autosave.clear();
// Submit logic...
};
</script>

<template>
<form ref="formRef" @submit.prevent="handleSubmit">
<!-- form fields -->
</form>
</template>

Angular

import { Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { attachFormAutosave, FormAutosaveHandle } from '@form-guardian/dom';

@Component({
selector: 'app-my-form',
template: `<form #myForm (submit)="onSubmit($event)">...</form>`
})
export class MyFormComponent implements OnInit, OnDestroy {
@ViewChild('myForm') formElement!: ElementRef;
private autosave: FormAutosaveHandle | null = null;

ngOnInit() {
this.autosave = attachFormAutosave({
formId: 'my-form',
root: this.formElement.nativeElement,
autoRestore: true,
});
}

ngOnDestroy() {
if (this.autosave) {
this.autosave.destroy();
}
}

async onSubmit(event: Event) {
event.preventDefault();
await this.autosave?.clear();
// Submit logic...
}
}