URL.createObjectURL(blob) returns a browser-managed resource address rather than encoded file contents. Losing the local string variable does not revoke that address. A long-running tool that generates previews needs a policy for retiring obsolete results and releasing their URLs.
A short address can retain a large resource
Think of three layers: the application string, the browser URL registration and the backing Blob data. Storage may be memory-backed or otherwise managed by the browser. String length therefore says little about the retained resource cost.
Two createObjectURL calls on the same Blob produce independent addresses. They need not duplicate all underlying bytes, but revoking one does not retire the other. Keep a tracked URL per result instead of generating fresh addresses during every render.
What revocation does
| Action | Effect | Does not guarantee |
|---|---|---|
| revokeObjectURL(url) | Removes this address mapping | Deletion of downloaded files |
| Drop a Blob reference | Removes one JS retaining path | Revocation of other URLs |
| Remove a media element | Ends that UI consumer | Immediate native-cache release |
| Close the document | Usually releases its URL registrations | Cleanup during a long session |
Do not start a new read from a revoked address. Revocation is not a forced garbage collection call: other Blob references, decoded surfaces or active consumers may retain resources. A process-memory number need not fall immediately. Evaluate repeated-use behavior rather than expecting an instant drop after one function call.
Make the result own its preview
The following example owns one image and one download link and returns an idempotent disposer. It expects a PNG Blob. Keep the disposer with that result and call it when replacing, clearing or unmounting the view. Calling it immediately after mounting would invalidate the URL before asynchronous consumers are ready.
function mountBlobPreview(blob: Blob, host: HTMLElement): () => void {
const url = URL.createObjectURL(blob);
const image = document.createElement('img');
const download = document.createElement('a');
image.alt = 'Generated image preview';
image.src = url;
download.href = url;
download.download = 'result.png'; // This example expects a PNG Blob.
download.textContent = 'Download PNG';
host.append(image, download);
let disposed = false;
return () => {
if (disposed) return;
disposed = true;
image.removeAttribute('src');
download.removeAttribute('href');
image.remove();
download.remove();
URL.revokeObjectURL(url);
};
}
// Keep this disposer with the current result.
const disposePreview = mountBlobPreview(pngBlob, resultContainer);
// Call disposePreview() when this result is retired or the view unmounts,
// not immediately after mounting or starting a download.In React, create the address inside an effect and let that effect cleanup revoke exactly the address it created. Creating URLs as untracked render-time side effects makes abandoned renders difficult to clean up. Async output also needs task identity checks so a late result cannot reinstall an obsolete preview after cancellation.
Loaded does not always mean unused
An image load event is not necessarily the end of the address lifetime. A visible preview may still need right-click saving or opening in another tab, and other elements may share its address. One-shot decoding with no remaining consumers has a different retirement point from an interactive result. Video can also continue reading or seeking.
Downloads do not provide a universal completion callback
An anchor click starts an action; it is not a promise that the download has consumed the URL. Immediate revocation can race that consumption. A timeout is a heuristic rather than proof of completion. For a result page, retaining a usable download link until the result is retired also lets users retry. Test starting a download and switching results in the target browsers.
When a product needs explicit write completion, a supported, user-authorized file writing API can provide a close operation to await. That is a different compatibility and interaction choice from an ordinary download anchor. Do not silently turn an active download affordance into a broken address just to minimize a reference count.
Audit repeated use
- Track URL creation and revocation by result ID without logging sensitive Blob contents.
- Generate, replace and clear ten times; verify every retired result is revoked and active results are bounded.
- Exercise preview, download, context-menu save, cancellation and navigation to detect premature cleanup.
- Observe process-memory trends; investigate retained Blobs, bitmaps, canvases and Workers if usage still grows.