Problem
When users insert images into the web comment editor, we currently use data URLs (base64 encoding) to store images in drafts. This allows unauthenticated users to create drafts with images that persist across sessions without uploading to IPFS until the comment is published. However, the current data URL approach has significant UX issues:
Browser Freezing: Base64 encoding blocks the main thread, causing the editor to freeze, especially with larger images (>1MB)
Performance: Data URLs are 33% larger than the original files, slowing down draft serialization and deserialization
Poor Mobile Experience: The encoding delay is even more noticeable on mobile devices
Why We Can't Just Upload Immediately
Unauthenticated Access: Anyone can create comment drafts without an account
Orphaned Uploads: If we upload images immediately, users who add images but never publish would create orphaned IPFS data
Resource Abuse: Malicious users could paste many images and waste daemon resources
Solution
I asked AI as suggested and this is what it came up with:
Option 1: Blob URLs ❌ NOT GOOD for your use case
const displaySrc = URL.createObjectURL(img) // "blob:http://..."Problems:
Blob URLs are temporary - they die when the page reloads
Can't be serialized to localStorage/IndexedDB
Lost when user closes the tab
Your cached drafts would lose images!
Verdict: Won't work for persistent drafts.
Option 2: IndexedDB with Blob Storage ✅ BEST for your use case
Store the actual File/Blob in IndexedDB, not the base64:
// Store original file in IndexedDB
const imageId = `draft-img-${Date.now()}`
await indexedDB.put('draft-images', img, imageId)
// Create blob URL for display (fast!)
const displaySrc = URL.createObjectURL(img)
node.create({
displaySrc: displaySrc, // Temporary display (fast)
imageId: imageId, // Reference to IndexedDB
name: name,
})
// On mount/reload: recreate blob URL from IndexedDB
const storedBlob = await indexedDB.get('draft-images', imageId)
if (storedBlob) {
const displaySrc = URL.createObjectURL(storedBlob)
// Update block with new blob URL
}Pros:
Fast - no base64 encoding
Persistent - survives page reload
Secure - stays in user's browser, no server upload
Efficient - stores binary, not base64
Cleanup - can delete IndexedDB entries when draft is published/discarded
Cons:
More code complexity
Need to handle IndexedDB lifecycle
Option 3: Data URLs (Current approach) ⚠️ Works but slow
const dataUrl = await readAsDataURL(img) // "data:image/jpeg;base64,..."Pros:
Serializable (can store in localStorage)
Persistent
Simple
Cons:
Very slow - base64 encoding blocks UI
33% larger than original file
Freezes browser for large images
Option 4: Upload Immediately ⚠️ Orphan problem
Cons:
Creates orphaned IPFS uploads
Wastes daemon resources
Security issue - unauthenticated users can fill your IPFS
My Recommendation: IndexedDB (Option 2)
Here's why:
Solves the freezing problem - no base64 encoding
Keeps drafts secure - client-side only until published
Persists across sessions - user can reload and continue
Clean architecture - natural place to store draft data
Easy cleanup - delete from IndexedDB when draft published/discarded