Autosave File Uploads
File uploads require special handling for autosave. This guide shows you how to implement it properly.
Important Considerations
Note: Form Guardian cannot directly save file objects to IndexedDB. However, you can:
- Save file metadata (name, size, type)
- Use FileReader API to save file data as base64 (for small files)
- Upload files immediately and save file URLs
- Use IndexedDB File API (for larger files)
Save File Metadata Only
For most cases, saving file metadata is sufficient:
import { useState } from 'react';
import { useFormAutosave } from '@form-guardian/react';
interface FileMetadata {
name: string;
size: number;
type: string;
lastModified: number;
}
function FileUploadForm() {
const [files, setFiles] = useState<FileMetadata[]>([]);
const [fileInputs, setFileInputs] = useState<File[]>([]);
const { formRef, restoreValues, clearDraft } = useFormAutosave(
'file-upload-form',
{ autoRestore: false }
);
useEffect(() => {
restoreValues(
(field, value) => {
if (field === 'files' && Array.isArray(value)) {
setFiles(value);
// Note: Actual File objects cannot be restored
// User will need to re-select files
}
},
() => ({
files: files,
})
);
}, []);
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFiles = Array.from(e.target.files || []);
const metadata = selectedFiles.map(file => ({
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified,
}));
setFiles(metadata);
setFileInputs(selectedFiles);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Upload files
await uploadFiles(fileInputs);
await clearDraft();
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input
type="file"
multiple
onChange={handleFileChange}
/>
{files.length > 0 && (
<div>
<p>Selected files (will need to be re-selected after restore):</p>
<ul>
{files.map((file, index) => (
<li key={index}>
{file.name} ({file.size} bytes)
</li>
))}
</ul>
</div>
)}
<button type="submit">Submit</button>
</form>
);
}
Save Small Files as Base64
For small files (< 5MB), you can save them as base64:
function FileUploadWithBase64() {
const [files, setFiles] = useState<{ name: string; data: string; type: string }[]>([]);
const { formRef, restoreValues, clearDraft } = useFormAutosave(
'file-upload-base64',
{ autoRestore: false }
);
const convertToBase64 = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onerror = reject;
reader.readAsDataURL(file);
});
};
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFiles = Array.from(e.target.files || []);
const convertedFiles = await Promise.all(
selectedFiles.map(async (file) => ({
name: file.name,
data: await convertToBase64(file),
type: file.type,
}))
);
setFiles(convertedFiles);
};
useEffect(() => {
restoreValues(
(field, value) => {
if (field === 'files' && Array.isArray(value)) {
setFiles(value);
}
},
() => ({ files })
);
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Convert base64 back to files if needed
await clearDraft();
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input type="file" multiple onChange={handleFileChange} />
{files.map((file, index) => (
<img key={index} src={file.data} alt={file.name} style={{ maxWidth: '200px' }} />
))}
<button type="submit">Submit</button>
</form>
);
}
Upload Immediately and Save URLs
Best approach for production: upload files immediately and save URLs:
function FileUploadWithURLs() {
const [fileUrls, setFileUrls] = useState<string[]>([]);
const [uploading, setUploading] = useState(false);
const { formRef, restoreValues, clearDraft } = useFormAutosave(
'file-upload-urls',
{ autoRestore: true }
);
useEffect(() => {
restoreValues(
(field, value) => {
if (field === 'fileUrls' && Array.isArray(value)) {
setFileUrls(value);
}
},
() => ({ fileUrls })
);
}, []);
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);
setUploading(true);
try {
// Upload files immediately
const urls = await Promise.all(
files.map(async (file) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const { url } = await response.json();
return url;
})
);
setFileUrls([...fileUrls, ...urls]);
} finally {
setUploading(false);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Submit form with file URLs
await clearDraft();
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input
type="file"
multiple
onChange={handleFileChange}
disabled={uploading}
/>
{uploading && <p>Uploading files...</p>}
{fileUrls.map((url, index) => (
<div key={index}>
<img src={url} alt={`Upload ${index}`} style={{ maxWidth: '200px' }} />
</div>
))}
<button type="submit">Submit</button>
</form>
);
}
With React Hook Form
Using React Hook Form for file uploads:
import { useForm } from 'react-hook-form';
import { useFormAutosave } from '@form-guardian/react';
import { useEffect } from 'react';
interface FormData {
title: string;
fileUrls: string[];
}
function FileUploadWithRHF() {
const { register, handleSubmit, setValue, getValues, watch } = useForm<FormData>({
defaultValues: {
title: '',
fileUrls: [],
},
});
const { formRef, restoreValues, clearDraft } = useFormAutosave(
'file-upload-rhf',
{ autoRestore: false }
);
useEffect(() => {
restoreValues(
(field, value) => {
setValue(field as keyof FormData, value, { shouldValidate: false });
},
() => getValues()
);
}, []);
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);
// Upload and get URLs
const urls = await uploadFiles(files);
setValue('fileUrls', [...getValues('fileUrls'), ...urls]);
};
const onSubmit = async (data: FormData) => {
await submitForm(data);
await clearDraft();
};
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<input {...register('title', { required: true })} placeholder="Title" />
<input type="file" multiple onChange={handleFileChange} />
<button type="submit">Submit</button>
</form>
);
}
Best Practices
- Upload files immediately - Don't wait for form submission
- Save file URLs - Not file objects
- Handle upload errors - Show user-friendly error messages
- Show upload progress - Let users know files are uploading
- Validate file size - Prevent saving huge files
- Clear drafts after submission - Remove file references
Common Pitfalls
❌ Don't Try to Save File Objects Directly
// ❌ Wrong - File objects cannot be serialized to IndexedDB
const file = e.target.files[0];
saveDraft({ file }); // Won't work
✅ Save File URLs or Metadata
// ✅ Correct - Save URLs or metadata
const url = await uploadFile(file);
saveDraft({ fileUrl: url });
Next Steps
- 📖 Getting Started - Basic setup
- 🍳 PWA Offline Mode - Handle offline file uploads
- 📚 API Reference - Complete API documentation