Web APIsPerformanceMedium

Blob URLs vs Data URLs — when to use which?

01

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.

02

What Each Looks Like

url-comparison.tstypescript
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
03

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
04

When to Use Which

when-to-use.tstypescript
// ✅ 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));
when-to-use-data-url.tstypescript
// ✅ 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}" />`;
05

Performance Comparison

performance.tstypescript
// 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.

06

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-management.tstypescript
// ❌ 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.

07

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 ✓