A powerful Vue 3 composable that automatically saves form data with intelligent debouncing, field filtering, and lifecycle hooks.
- π Auto-save on change with configurable debounce
- π― Smart field filtering - skip Inertia helpers or custom fields
- π‘οΈ Blockable watchers - pause auto-save during initialization
- π Lifecycle hooks - beforeSave, afterSave, onError callbacks
- ποΈ Custom serialization - support for circular references and functions
- π§ͺ Custom comparators - shallow/deep equality without stringification
- π§Ή Automatic cleanup - no memory leaks on component unmount
- π¦ Framework agnostic - works with Axios, Inertia, Fetch, etc.
npm install @provydon/vue-auto-save
<script setup>
import { reactive } from 'vue'
import { useAutoSaveForm } from '@provydon/vue-auto-save'
const form = reactive({
title: '',
content: '',
tags: []
})
const { isAutoSaving } = useAutoSaveForm(form, {
onSave: async () => {
await axios.post('/api/posts', form)
},
debounce: 2000,
debug: true
})
</script>
<template>
<form>
<input v-model="form.title" placeholder="Post title" />
<textarea v-model="form.content" placeholder="Post content" />
<div v-if="isAutoSaving">Saving...</div>
</form>
</template>
const { isAutoSaving, blockWatcher, unblockWatcher, stop } = useAutoSaveForm(
form, // reactive object or ref
options
)
Option | Type | Default | Description |
---|---|---|---|
onSave |
() => void | Promise<void> |
Required | Function called when auto-save should trigger |
debounce |
number |
3000 |
Delay in milliseconds before saving |
skipFields |
string[] |
[] |
Field names to exclude from tracking |
skipInertiaFields |
boolean |
true |
Skip common Inertia.js form helpers |
deep |
boolean |
true |
Deep watch the form object |
debug |
boolean |
false |
Enable console logging |
saveOnInit |
boolean |
false |
Save immediately on mount |
serialize |
(obj) => string |
JSON.stringify |
Custom serialization function |
compare |
(a, b) => boolean |
undefined |
Custom comparison function |
onBeforeSave |
() => void |
undefined |
Called before saving |
onAfterSave |
() => void |
undefined |
Called after successful save |
onError |
(err) => void |
undefined |
Called on save error |
Property | Type | Description |
---|---|---|
isAutoSaving |
Ref<boolean> |
Reactive boolean indicating save status |
blockWatcher |
(ms?: number) => void |
Temporarily block auto-save |
unblockWatcher |
(ms?: number | null) => void |
Unblock and optionally save immediately |
stop |
() => void |
Manually stop the watcher |
import { useForm } from '@inertiajs/vue3'
import { useAutoSaveForm } from '@provydon/vue-auto-save'
const form = useForm({
title: '',
content: '',
published: false
})
const { isAutoSaving } = useAutoSaveForm(form, {
onSave: () => form.post('/posts', { preserveState: true }),
skipInertiaFields: true, // Skips processing, errors, etc.
debounce: 1000
})
const { isAutoSaving } = useAutoSaveForm(form, {
onSave: saveToAPI,
serialize: (obj) => {
// Handle circular references or functions
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'function') return '[Function]'
return value
})
}
})
import { isEqual } from 'lodash-es'
const { isAutoSaving } = useAutoSaveForm(form, {
onSave: saveToAPI,
compare: (a, b) => isEqual(a, b), // Deep equality without stringification
serialize: undefined // Not used when compare is provided
})
const { isAutoSaving, blockWatcher } = useAutoSaveForm(form, {
onSave: saveToAPI,
saveOnInit: false
})
// Block auto-save during form initialization
blockWatcher(5000) // Block for 5 seconds
// Or block indefinitely and unblock manually
blockWatcher()
// ... do initialization work ...
unblockWatcher() // Resume auto-save
const form = ref({
name: '',
email: ''
})
const { isAutoSaving } = useAutoSaveForm(form, {
onSave: saveToAPI
})
const { isAutoSaving } = useAutoSaveForm(form, {
onSave: saveToAPI,
onBeforeSave: () => {
console.log('About to save...')
},
onAfterSave: () => {
console.log('Save completed!')
},
onError: (error) => {
console.error('Save failed:', error)
}
})
const { isAutoSaving, stop } = useAutoSaveForm(form, {
onSave: saveToAPI
})
// Manually stop the watcher
stop()
// The watcher will also stop automatically on component unmount
<template>
<div class="form-container">
<input v-model="form.title" />
<div v-if="isAutoSaving" class="save-indicator">
<span class="spinner"></span>
Auto-saving...
</div>
</div>
</template>
<style scoped>
.save-indicator {
display: flex;
align-items: center;
gap: 8px;
color: #666;
font-size: 14px;
}
.spinner {
width: 16px;
height: 16px;
border: 2px solid #ddd;
border-top: 2px solid #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
- Circular References:
JSON.stringify
(default serializer) will throw on circular references. Use a customserialize
function if needed. - Functions: Functions won't survive
JSON.stringify
. Use custom serialization for function-heavy forms. - Vue Version: Requires Vue 3.2+ (supports both reactive objects and refs).
- Cleanup: Watchers and timers are automatically cleaned up on component unmount.
Contributions are welcome! Please feel free to submit a Pull Request.
MIT Β© Providence Ifeosame
If you find this package helpful, consider:
- β Starring the repository
- π Reporting bugs
- π‘ Suggesting features
- β Buying me a coffee