Blob URLs vs Data URLs — when to use which?
The Short Answer
A Blob URL (created via URL.createObjectURL()) is a temporary reference to a Blob in memory. A Data URL (like data:image/png;base64,...) embeds the actual data inline as a base64-encoded string. They solve similar problems but have very different performance characteristics and use cases.
What Each Looks Like
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
// Blob URL — short, just a pointer
const blobURL = URL.createObjectURL(blob);
console.log(blobURL);
// "blob:http://localhost:3000/4a6f8b2c-1234-5678-9abc-def012345678"
// ↑ tiny string, data stays in memory
// Data URL — the actual data encoded inline
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsDataURL(blob);
// "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="
// ↑ the ENTIRE content is in the string itself
How They Work
Think of a Blob URL as a library card — it's a short reference that points to a book (the Blob) sitting on a shelf (in browser memory). A Data URL is like photocopying the entire book and carrying it around — the data is right there in the string.
Blob URL
- ✅Creates a short URL string that points to the Blob in memory
- ✅The Blob stays in memory until you call `URL.revokeObjectURL()` or the page unloads
- ✅Only works in the same origin/session — can't share across tabs or save to a database
- ✅Fast to create, no encoding overhead, works with any size
Data URL
- ❌Encodes the entire binary content as a base64 string inside the URL
- ❌Base64 increases size by ~33% (1 MB file → ~1.33 MB string)
- ❌Self-contained — can be saved to a database, embedded in CSS/HTML, shared anywhere
- ❌Slow to create for large files, can freeze the main thread during encoding
When to Use Which
// ✅ USE BLOB URLs FOR:
// 1. Image/video previews (fast, no encoding delay)
const file = inputElement.files![0];
const previewURL = URL.createObjectURL(file);
img.src = previewURL; // Instant preview, even for 50MB images
// 2. Client-side file downloads
const csvBlob = new Blob([csvData], { type: 'text/csv' });
const downloadURL = URL.createObjectURL(csvBlob);
anchor.href = downloadURL;
anchor.download = 'report.csv';
// 3. Playing audio/video from blobs
const audioBlob = await fetch('/api/audio').then(r => r.blob());
audioElement.src = URL.createObjectURL(audioBlob);
// 4. Web Workers from inline code
const workerCode = new Blob([`self.onmessage = (e) => { ... }`], { type: 'text/javascript' });
const worker = new Worker(URL.createObjectURL(workerCode));
// ✅ USE DATA URLs FOR:
// 1. Small images you want to embed in CSS/HTML (icons, tiny thumbnails)
// Saves an HTTP request
const tinyIcon = `data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><circle r="5"/></svg>`;
// 2. Storing image data in a database or JSON payload
const reader = new FileReader();
reader.onload = () => {
const dataURL = reader.result as string;
// "data:image/png;base64,iVBORw0KGgo..."
saveToDatabase({ avatar: dataURL }); // Portable, self-contained
};
reader.readAsDataURL(smallThumbnail);
// 3. Canvas snapshots for thumbnails
const dataURL = canvas.toDataURL('image/jpeg', 0.5);
// Can be stored, displayed, or sent over the network
// 4. Embedding in emails or PDFs (no external references needed)
const emailHtml = `<img src="${dataURL}" />`;
Performance Comparison
// Let's compare with a 5MB image file
const largeFile = input.files![0]; // 5 MB image
// Blob URL: instant ⚡
console.time('blobURL');
const blobURL = URL.createObjectURL(largeFile);
console.timeEnd('blobURL'); // ~0.01ms
// Data URL: slow 🐌 (must read + base64 encode entire file)
console.time('dataURL');
const reader = new FileReader();
reader.onload = () => {
console.timeEnd('dataURL'); // ~200-500ms for 5MB!
// The resulting string is ~6.7 MB (33% overhead)
console.log(reader.result!.length); // ~6,700,000 characters
};
reader.readAsDataURL(largeFile);
Rule of thumb
Use Blob URLs for anything the user will see/interact with in the current session (previews, downloads, playback). Use Data URLs only for small data (<50KB) that needs to be portable or embedded.
Memory Management
This is where most developers slip up. Blob URLs pin the Blob in memory — the garbage collector can't free it even if you lose all other references. You MUST manually revoke them.
// ❌ Memory leak — Blob URL keeps the 50MB file in memory forever
function showPreview(file: File) {
const url = URL.createObjectURL(file);
img.src = url;
// url is never revoked → 50MB stays in memory until page unloads
}
// ✅ Correct — revoke after use
function showPreview(file: File) {
const url = URL.createObjectURL(file);
img.src = url;
img.onload = () => URL.revokeObjectURL(url); // Free memory after image loads
}
// ✅ For downloads — revoke after a short delay
function downloadBlob(blob: Blob, filename: string) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
// Revoke after browser has grabbed the data
setTimeout(() => URL.revokeObjectURL(url), 1000);
}
Data URLs don't need cleanup
Data URLs are just strings — they get garbage collected like any other string when no references remain. No manual cleanup needed. This is one advantage of Data URLs for simple cases.
Quick Reference Table
Quick Revision Cheat Sheet
Creation speed: Blob URL: instant | Data URL: slow (must encode)
Size overhead: Blob URL: none | Data URL: +33% from base64
Portability: Blob URL: same session only | Data URL: save/share anywhere
Memory: Blob URL: must revoke manually | Data URL: auto GC'd
Max size: Blob URL: unlimited | Data URL: browser-dependent (often 2MB limit in some contexts)
Use for previews: Blob URL ✓
Use for database storage: Data URL ✓
Use for large files: Blob URL ✓