DEV Community

Cover image for Blob in JavaScript: A Practical Deep Dive with Real-World Examples
Abanoub Kerols
Abanoub Kerols

Posted on

Blob in JavaScript: A Practical Deep Dive with Real-World Examples


If you work with modern web applications, sooner or later you'll need to handle data that isn't simply a JavaScript string or JSON object.

You may need to:

  • Preview an uploaded image before sending it to the server.
  • Generate a CSV file directly in the browser.
  • Download a PDF returned by an API.
  • Convert generated data into a file.
  • Upload binary data using fetch().
  • Display an image or PDF without saving it first.
  • Process audio or video data.

This is where the Blob API becomes extremely useful.

In this article, we'll go beyond the basic definition of Blob.

We'll understand how Blob works, how it relates to File, ArrayBuffer, and typed arrays, and we'll build a complete practical example involving:

Selecting an image → converting it to a Blob → previewing it → uploading it → receiving a Blob from an API → downloading it.


Table of Contents


What Is a Blob?

Blob stands for:

Binary Large Object

A Blob represents immutable raw binary data.

That data could represent:

  • an image
  • a PDF
  • a video
  • an audio file
  • a CSV file
  • plain text
  • JSON
  • arbitrary binary data

For example:

const blob = new Blob(
  ["Hello, world!"],
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

We have created a Blob containing:

Hello, world!
Enter fullscreen mode Exit fullscreen mode

and told the browser:

This data is text/plain.
Enter fullscreen mode Exit fullscreen mode

We can inspect it:

console.log(blob.size);
console.log(blob.type);
Enter fullscreen mode Exit fullscreen mode

The result will be approximately:

13
text/plain
Enter fullscreen mode Exit fullscreen mode

Why approximately?

Because size represents the number of bytes, not necessarily the number of JavaScript characters.

For ASCII text such as "Hello, world!", the number happens to match.

For Unicode text, it can be different:

const blob = new Blob(
  ["Hello 👋"],
  {
    type: "text/plain"
  }
);

console.log(blob.size);
Enter fullscreen mode Exit fullscreen mode

The browser is measuring the encoded binary representation.


Why Do We Need Blob?

JavaScript applications frequently communicate using JSON:

fetch("/api/users")
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });
Enter fullscreen mode Exit fullscreen mode

But not everything is JSON.

Imagine your backend returns:

report.pdf
Enter fullscreen mode Exit fullscreen mode

or:

profile-image.jpg
Enter fullscreen mode Exit fullscreen mode

or:

invoice.xlsx
Enter fullscreen mode Exit fullscreen mode

The browser needs a representation for that binary content.

That's where Blob becomes useful.

A simplified flow looks like this:

flowchart LR
    A[Server] --> B[Binary Data]
    B --> C[Blob]
    C --> D[Preview]
    C --> E[Download]
    C --> F[Upload]
    C --> G[Convert to File]

Blob is therefore especially useful at the boundary between:

Web APIs and binary data


Creating a Blob

The Blob constructor has the following general structure:

new Blob(blobParts, options)
Enter fullscreen mode Exit fullscreen mode

For example:

const blob = new Blob(
  ["Hello Blob!"],
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

Let's inspect it:

console.log(blob);
Enter fullscreen mode Exit fullscreen mode

You can also inspect:

console.log(blob.size);
console.log(blob.type);
Enter fullscreen mode Exit fullscreen mode

Blob Parts

The first argument is an array of parts.

Those parts can contain different types of data.

For example:

const blob = new Blob([
  "Hello ",
  "world!"
]);
Enter fullscreen mode Exit fullscreen mode

The resulting Blob contains:

Hello world!
Enter fullscreen mode Exit fullscreen mode

You can also combine multiple strings:

const blob = new Blob([
  "Hello\n",
  "This is a Blob\n",
  "Created in JavaScript."
], {
  type: "text/plain"
});
Enter fullscreen mode Exit fullscreen mode

Blob Can Contain Other Blobs

You can also combine existing Blobs:

const first = new Blob(
  ["Hello "],
  { type: "text/plain" }
);

const second = new Blob(
  ["world!"],
  { type: "text/plain" }
);

const combined = new Blob([
  first,
  second
], {
  type: "text/plain"
});
Enter fullscreen mode Exit fullscreen mode

Now:

console.log(await combined.text());
Enter fullscreen mode Exit fullscreen mode

Result:

Hello world!
Enter fullscreen mode Exit fullscreen mode

The MIME Type

The type property represents the Blob's MIME type.

For example:

const blob = new Blob(
  ["Hello"],
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

Then:

console.log(blob.type);
Enter fullscreen mode Exit fullscreen mode

returns:

text/plain
Enter fullscreen mode Exit fullscreen mode

For an image:

const imageBlob = new Blob(
  [imageData],
  {
    type: "image/png"
  }
);
Enter fullscreen mode Exit fullscreen mode

For a PDF:

const pdfBlob = new Blob(
  [pdfData],
  {
    type: "application/pdf"
  }
);
Enter fullscreen mode Exit fullscreen mode

For JSON:

const jsonBlob = new Blob(
  [JSON.stringify({
    name: "Abanoub"
  })],
  {
    type: "application/json"
  }
);
Enter fullscreen mode Exit fullscreen mode

Common MIME types include:

Content MIME Type
Plain text text/plain
HTML text/html
JSON application/json
PDF application/pdf
PNG image/png
JPEG image/jpeg
GIF image/gif
WebP image/webp
CSV text/csv
ZIP application/zip
MP3 audio/mpeg
MP4 video/mp4

The MIME type doesn't magically transform the data.

It is metadata describing what the data represents.


Blob Properties

A Blob mainly exposes two important properties.

size

size returns the size of the Blob in bytes.

const blob = new Blob(
  ["Hello"]
);

console.log(blob.size);
Enter fullscreen mode Exit fullscreen mode

Result:

5
Enter fullscreen mode Exit fullscreen mode

type

type returns the MIME type.

const blob = new Blob(
  ["Hello"],
  {
    type: "text/plain"
  }
);

console.log(blob.type);
Enter fullscreen mode Exit fullscreen mode

Result:

text/plain
Enter fullscreen mode Exit fullscreen mode

Reading Blob Data

Creating a Blob is only part of the story.

Eventually, you usually need to consume the data.

The most important methods are:

blob.text()
blob.arrayBuffer()
blob.stream()
Enter fullscreen mode Exit fullscreen mode

Blob.text()

Use text() when the Blob contains textual data.

Example:

const blob = new Blob(
  ["Hello from Blob!"],
  {
    type: "text/plain"
  }
);

const text = await blob.text();

console.log(text);
Enter fullscreen mode Exit fullscreen mode

Output:

Hello from Blob!
Enter fullscreen mode Exit fullscreen mode

Because text() returns a Promise, you can use:

blob.text().then(text => {
  console.log(text);
});
Enter fullscreen mode Exit fullscreen mode

Or preferably:

const text = await blob.text();
Enter fullscreen mode Exit fullscreen mode

Reading JSON from a Blob

Suppose you have:

const data = {
  name: "Abanoub",
  role: "Frontend Developer"
};

const blob = new Blob(
  [JSON.stringify(data)],
  {
    type: "application/json"
  }
);
Enter fullscreen mode Exit fullscreen mode

You can read it:

const text = await blob.text();

const parsed = JSON.parse(text);

console.log(parsed.name);
console.log(parsed.role);
Enter fullscreen mode Exit fullscreen mode

Output:

Abanoub
Frontend Developer
Enter fullscreen mode Exit fullscreen mode

Blob.arrayBuffer()

If you need the actual binary representation, use:

blob.arrayBuffer()
Enter fullscreen mode Exit fullscreen mode

Example:

const blob = new Blob(
  ["Hello"],
  {
    type: "text/plain"
  }
);

const buffer = await blob.arrayBuffer();

console.log(buffer);
Enter fullscreen mode Exit fullscreen mode

You get an:

ArrayBuffer
Enter fullscreen mode Exit fullscreen mode

This becomes particularly useful when working with:

  • binary protocols
  • cryptography
  • Web APIs
  • Web Audio
  • WebSockets
  • binary file formats
  • typed arrays

Blob → ArrayBuffer → Uint8Array

A common pattern is:

const buffer = await blob.arrayBuffer();

const bytes = new Uint8Array(buffer);

console.log(bytes);
Enter fullscreen mode Exit fullscreen mode

For "Hello" you'll see byte values similar to:

[72, 101, 108, 108, 111]
Enter fullscreen mode Exit fullscreen mode

The relationship is:

flowchart LR
    A[Blob] -->|arrayBuffer| B[ArrayBuffer]
    B --> C[Uint8Array]
    C --> D[Individual Bytes]

Blob.stream()

A Blob can also expose its data as a ReadableStream.

const blob = new Blob(
  ["Hello from Blob"],
  {
    type: "text/plain"
  }
);

const stream = blob.stream();

console.log(stream);
Enter fullscreen mode Exit fullscreen mode

This becomes useful when dealing with streaming APIs and large amounts of data.

We'll return to this later.


Blob.slice()

slice() creates a new Blob containing a portion of the original Blob.

Example:

const blob = new Blob(
  ["Hello World"],
  {
    type: "text/plain"
  }
);

const part = blob.slice(0, 5);

console.log(await part.text());
Enter fullscreen mode Exit fullscreen mode

Result:

Hello
Enter fullscreen mode Exit fullscreen mode

The syntax is:

blob.slice(start, end, contentType)
Enter fullscreen mode Exit fullscreen mode

For example:

const part = blob.slice(
  0,
  5,
  "text/plain"
);
Enter fullscreen mode Exit fullscreen mode

This is useful when you need to work with only a portion of binary data.


Blob and Object URLs

One of the most useful concepts associated with Blob is:

URL.createObjectURL()
Enter fullscreen mode Exit fullscreen mode

Suppose you have:

const blob = new Blob(
  ["Hello"],
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

You can create a temporary URL:

const url = URL.createObjectURL(blob);

console.log(url);
Enter fullscreen mode Exit fullscreen mode

You'll get something similar to:

blob:https://example.com/550e8400-e29b-41d4-a716-446655440000
Enter fullscreen mode Exit fullscreen mode

This URL doesn't represent a file on your server.

It represents the Blob in the browser.


Think of It Like This

flowchart LR
    A[Blob in Memory] --> B[URL.createObjectURL]
    B --> C[blob:// URL]
    C --> D[img]
    C --> E[a]
    C --> F[iframe]
    C --> G[video]

This is extremely useful.


Previewing an Image

Let's say the user selects an image.

HTML:

<input
  type="file"
  id="imageInput"
  accept="image/*"
/>

<img
  id="preview"
  width="300"
/>
Enter fullscreen mode Exit fullscreen mode

JavaScript:

const input = document.getElementById("imageInput");
const preview = document.getElementById("preview");

input.addEventListener("change", () => {
  const file = input.files[0];

  if (!file) {
    return;
  }

  const url = URL.createObjectURL(file);

  preview.src = url;
});
Enter fullscreen mode Exit fullscreen mode

Notice something important:

File is already a kind of Blob.

So we don't need to manually convert the selected file into another Blob just to preview it.


Blob URLs Are Temporary

An object URL consumes browser resources.

When you're finished with it, release it:

URL.revokeObjectURL(url);
Enter fullscreen mode Exit fullscreen mode

For example:

const url = URL.createObjectURL(file);

preview.src = url;

preview.onload = () => {
  URL.revokeObjectURL(url);
};
Enter fullscreen mode Exit fullscreen mode

This becomes important when you're creating many object URLs.


Downloading a Blob

One of the most common Blob use cases is generating a file dynamically.

Let's generate a text file.

const content = `
Name: Abanoub
Role: Frontend Developer
`;

const blob = new Blob(
  [content],
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

Create an object URL:

const url = URL.createObjectURL(blob);
Enter fullscreen mode Exit fullscreen mode

Create a link:

const link = document.createElement("a");

link.href = url;
link.download = "profile.txt";

link.click();
Enter fullscreen mode Exit fullscreen mode

Finally:

URL.revokeObjectURL(url);
Enter fullscreen mode Exit fullscreen mode

Complete example:

function downloadTextFile() {
  const content = `
Name: Abanoub
Role: Frontend Developer
`;

  const blob = new Blob(
    [content],
    {
      type: "text/plain"
    }
  );

  const url = URL.createObjectURL(blob);

  const link = document.createElement("a");

  link.href = url;
  link.download = "profile.txt";

  link.click();

  URL.revokeObjectURL(url);
}
Enter fullscreen mode Exit fullscreen mode

This creates a real downloadable file without sending anything to a server.


Generating a CSV File

Blob becomes particularly useful for exporting data.

Imagine:

const users = [
  {
    name: "Abanoub",
    email: "abanoub@example.com"
  },
  {
    name: "John",
    email: "john@example.com"
  }
];
Enter fullscreen mode Exit fullscreen mode

We can generate CSV:

const csv = [
  "name,email",
  ...users.map(user =>
    `${user.name},${user.email}`
  )
].join("\n");
Enter fullscreen mode Exit fullscreen mode

Then:

const blob = new Blob(
  [csv],
  {
    type: "text/csv;charset=utf-8"
  }
);
Enter fullscreen mode Exit fullscreen mode

Create the download:

const url = URL.createObjectURL(blob);

const link = document.createElement("a");

link.href = url;
link.download = "users.csv";

link.click();

URL.revokeObjectURL(url);
Enter fullscreen mode Exit fullscreen mode

The browser will download:

users.csv
Enter fullscreen mode Exit fullscreen mode

Blob vs File

This is one of the most important distinctions.

A File is essentially a specialized kind of Blob.

Conceptually:

Blob
  ↑
  |
 File
Enter fullscreen mode Exit fullscreen mode

A Blob primarily represents binary data.

A File represents binary data plus file-oriented metadata, such as:

  • filename
  • last modified timestamp

For example:

const file = new File(
  ["Hello"],
  "hello.txt",
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

We can inspect:

console.log(file.name);
console.log(file.size);
console.log(file.type);
console.log(file.lastModified);
Enter fullscreen mode Exit fullscreen mode

File Is a Blob

You can check:

const file = new File(
  ["Hello"],
  "hello.txt",
  {
    type: "text/plain"
  }
);

console.log(file instanceof Blob);
Enter fullscreen mode Exit fullscreen mode

Result:

true
Enter fullscreen mode Exit fullscreen mode

That's why APIs that accept Blob often also accept File.

For example:

function processBlob(blob) {
  console.log(blob.size);
}
Enter fullscreen mode Exit fullscreen mode

You can pass:

processBlob(file);
Enter fullscreen mode Exit fullscreen mode

because File is a Blob.


Creating a File from a Blob

Suppose we already have:

const blob = new Blob(
  ["Hello from JavaScript"],
  {
    type: "text/plain"
  }
);
Enter fullscreen mode Exit fullscreen mode

We can turn it into a File:

const file = new File(
  [blob],
  "hello.txt",
  {
    type: blob.type
  }
);
Enter fullscreen mode Exit fullscreen mode

Now:

console.log(file.name);
Enter fullscreen mode Exit fullscreen mode

returns:

hello.txt
Enter fullscreen mode Exit fullscreen mode

Uploading a Blob

You can send a Blob directly using fetch().

Example:

const blob = new Blob(
  ["Hello from the browser"],
  {
    type: "text/plain"
  }
);

const response = await fetch(
  "/api/upload",
  {
    method: "POST",
    body: blob
  }
);
Enter fullscreen mode Exit fullscreen mode

The request body contains the Blob data.

You can also explicitly specify headers:

await fetch(
  "/api/upload",
  {
    method: "POST",
    headers: {
      "Content-Type": "text/plain"
    },
    body: blob
  }
);
Enter fullscreen mode Exit fullscreen mode

Uploading a File Using FormData

For normal file uploads, FormData is often more convenient.

HTML:

<input
  type="file"
  id="fileInput"
/>
Enter fullscreen mode Exit fullscreen mode

JavaScript:

const input = document.getElementById("fileInput");

const file = input.files[0];

const formData = new FormData();

formData.append(
  "file",
  file
);

await fetch(
  "/api/upload",
  {
    method: "POST",
    body: formData
  }
);
Enter fullscreen mode Exit fullscreen mode

Notice that we don't manually set:

Content-Type
Enter fullscreen mode Exit fullscreen mode

when using FormData.

The browser needs to generate the multipart boundary automatically.


Receiving a Blob from an API

This is another extremely important use case.

Suppose your backend exposes:

GET /api/report
Enter fullscreen mode Exit fullscreen mode

and returns a PDF.

Instead of:

const data = await response.json();
Enter fullscreen mode Exit fullscreen mode

you can do:

const response = await fetch(
  "/api/report"
);

const blob = await response.blob();
Enter fullscreen mode Exit fullscreen mode

Now you have the PDF as a Blob.

You can download it.

const url = URL.createObjectURL(blob);

const link = document.createElement("a");

link.href = url;
link.download = "report.pdf";

link.click();

URL.revokeObjectURL(url);
Enter fullscreen mode Exit fullscreen mode

Opening a PDF

You don't necessarily need to download it.

You can create an object URL:

const response = await fetch(
  "/api/report"
);

const blob = await response.blob();

const url = URL.createObjectURL(blob);
Enter fullscreen mode Exit fullscreen mode

Then:

window.open(url);
Enter fullscreen mode Exit fullscreen mode

Or put it inside an iframe:

<iframe id="pdfViewer"></iframe>
Enter fullscreen mode Exit fullscreen mode
const iframe = document.getElementById(
  "pdfViewer"
);

iframe.src = url;
Enter fullscreen mode Exit fullscreen mode

Complete Practical Example

Now let's build a small application that demonstrates the Blob workflow.

Our application will allow the user to:

  1. Select an image.
  2. Preview it.
  3. Convert/process the image as Blob data.
  4. Display Blob metadata.
  5. Download the image Blob.

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">

  <meta
    name="viewport"
    content="width=device-width, initial-scale=1.0"
  >

  <title>Blob Demo</title>
</head>

<body>

  <h1>Blob Image Demo</h1>

  <input
    type="file"
    id="imageInput"
    accept="image/*"
  />

  <br><br>

  <img
    id="preview"
    width="400"
    alt="Preview"
  >

  <p>
    Size:
    <span id="size"></span>
  </p>

  <p>
    Type:
    <span id="type"></span>
  </p>

  <button id="downloadButton">
    Download
  </button>

  <script src="app.js"></script>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

JavaScript

const imageInput =
  document.getElementById("imageInput");

const preview =
  document.getElementById("preview");

const sizeElement =
  document.getElementById("size");

const typeElement =
  document.getElementById("type");

const downloadButton =
  document.getElementById("downloadButton");

let currentBlob = null;
let currentUrl = null;
Enter fullscreen mode Exit fullscreen mode

When the user selects an image:

imageInput.addEventListener(
  "change",
  () => {

    const file =
      imageInput.files[0];

    if (!file) {
      return;
    }

    currentBlob = file;

    sizeElement.textContent =
      `${file.size} bytes`;

    typeElement.textContent =
      file.type;

    if (currentUrl) {
      URL.revokeObjectURL(currentUrl);
    }

    currentUrl =
      URL.createObjectURL(file);

    preview.src =
      currentUrl;
  }
);
Enter fullscreen mode Exit fullscreen mode

Then implement downloading:

downloadButton.addEventListener(
  "click",
  () => {

    if (!currentBlob) {
      alert("Please select an image first.");
      return;
    }

    const url =
      URL.createObjectURL(currentBlob);

    const link =
      document.createElement("a");

    link.href = url;

    link.download =
      "downloaded-image";

    link.click();

    URL.revokeObjectURL(url);
  }
);
Enter fullscreen mode Exit fullscreen mode

Understanding What Happened

The complete flow is:

flowchart TD
    A[User selects image] --> B[File object]
    B --> C[File is a Blob]
    C --> D[URL.createObjectURL]
    D --> E[Blob URL]
    E --> F[Image Preview]

    C --> G[Download]
    G --> H[Browser saves file]

The important point is that we didn't upload anything to the server.

The browser already has the file data.

We simply created a temporary URL pointing to that data.


A More Interesting Example: Generate an Image Blob

We can also create Blob data ourselves.

For example, using a Canvas:

const canvas =
  document.createElement("canvas");

canvas.width = 500;
canvas.height = 300;

const context =
  canvas.getContext("2d");

context.fillStyle = "black";

context.fillRect(
  0,
  0,
  500,
  300
);
Enter fullscreen mode Exit fullscreen mode

Now draw some text:

context.fillStyle = "white";

context.font =
  "30px Arial";

context.fillText(
  "Hello Blob!",
  150,
  150
);
Enter fullscreen mode Exit fullscreen mode

We can convert the canvas to a Blob:

canvas.toBlob(
  blob => {

    console.log(blob);

  },
  "image/png"
);
Enter fullscreen mode Exit fullscreen mode

Now we have:

Canvas
   ↓
PNG Blob
Enter fullscreen mode Exit fullscreen mode

Canvas → Blob → Download

We can combine everything:

canvas.toBlob(
  blob => {

    if (!blob) {
      return;
    }

    const url =
      URL.createObjectURL(blob);

    const link =
      document.createElement("a");

    link.href = url;

    link.download =
      "generated-image.png";

    link.click();

    URL.revokeObjectURL(url);
  },
  "image/png"
);
Enter fullscreen mode Exit fullscreen mode

This is a very practical browser capability.

You can generate an image entirely on the client and download it without a backend.


Blob and fetch()

The Fetch API provides several ways to consume a response.

For example:

const response = await fetch("/api/data");
Enter fullscreen mode Exit fullscreen mode

You might use:

await response.json();
Enter fullscreen mode Exit fullscreen mode

for JSON.

Or:

await response.text();
Enter fullscreen mode Exit fullscreen mode

for text.

Or:

await response.blob();
Enter fullscreen mode Exit fullscreen mode

for binary/file-like data.

Or:

await response.arrayBuffer();
Enter fullscreen mode Exit fullscreen mode

for lower-level binary processing.

A useful mental model is:

flowchart TD
    A[HTTP Response] --> B[JSON]
    A --> C[Text]
    A --> D[Blob]
    A --> E[ArrayBuffer]

Choose the representation based on what you need to do with the response.


Blob vs ArrayBuffer vs Uint8Array

This is an area that often confuses developers.

These concepts are related, but they serve different purposes.

Blob

Think:

"I have some data that I want to treat as a file-like binary object."

Example:

const blob = new Blob(
  [data],
  {
    type: "image/png"
  }
);
Enter fullscreen mode Exit fullscreen mode

Great for:

  • files
  • downloads
  • previews
  • uploads
  • media
  • browser APIs

ArrayBuffer

Think:

"I need access to raw binary memory."

Example:

const buffer =
  await blob.arrayBuffer();
Enter fullscreen mode Exit fullscreen mode

An ArrayBuffer represents a fixed-length block of binary memory.

It doesn't tell you what those bytes mean.


Uint8Array

Think:

"I want to access the individual bytes."

Example:

const bytes =
  new Uint8Array(buffer);

console.log(bytes[0]);
console.log(bytes[1]);
Enter fullscreen mode Exit fullscreen mode

Comparison

API Main Purpose
Blob File-like binary data
File Blob + filename/metadata
ArrayBuffer Raw binary memory
Uint8Array View/access individual bytes

A useful conceptual relationship is:

flowchart LR
    A[Blob] -->|arrayBuffer| B[ArrayBuffer]
    B --> C[Uint8Array]

But don't think of these as simply different names for the same thing.

They represent different abstractions.


Blob Streams

For large data, streaming can be important.

A Blob can produce a readable stream:

const blob = new Blob([
  "Large amount of data..."
]);

const stream =
  blob.stream();
Enter fullscreen mode Exit fullscreen mode

You can consume it using a reader:

const reader =
  stream.getReader();

while (true) {

  const {
    done,
    value
  } = await reader.read();

  if (done) {
    break;
  }

  console.log(value);
}
Enter fullscreen mode Exit fullscreen mode

The value contains chunks of data.

This is useful when you don't want to conceptually treat the entire resource as one immediately-consumed value.


Memory Management

Blob-related code often involves browser-managed resources.

One particularly important API is:

URL.createObjectURL()
Enter fullscreen mode Exit fullscreen mode

Whenever you create:

const url =
  URL.createObjectURL(blob);
Enter fullscreen mode Exit fullscreen mode

you should eventually consider:

URL.revokeObjectURL(url);
Enter fullscreen mode Exit fullscreen mode

For example:

const url =
  URL.createObjectURL(blob);

image.src = url;

image.onload = () => {
  URL.revokeObjectURL(url);
};
Enter fullscreen mode Exit fullscreen mode

Why Does revokeObjectURL() Matter?

Imagine a UI where users continuously select images:

Image 1 → createObjectURL()
Image 2 → createObjectURL()
Image 3 → createObjectURL()
Image 4 → createObjectURL()
...
Enter fullscreen mode Exit fullscreen mode

If you keep creating object URLs and never release them, the browser may retain resources longer than necessary.

A better pattern is:

if (currentUrl) {
  URL.revokeObjectURL(currentUrl);
}

currentUrl =
  URL.createObjectURL(newBlob);
Enter fullscreen mode Exit fullscreen mode

This is especially important in:

  • image galleries
  • file managers
  • video previews
  • drag-and-drop interfaces
  • React applications
  • Angular applications
  • long-lived single-page applications

Common Mistake #1: Confusing Blob With File

A Blob is not necessarily a file on disk.

This:

const blob = new Blob([
  "Hello"
]);
Enter fullscreen mode Exit fullscreen mode

doesn't mean you created:

hello.txt
Enter fullscreen mode Exit fullscreen mode

on the user's filesystem.

You've created binary data represented by a JavaScript object.

To create a downloadable file, you need to explicitly provide a filename through the download mechanism or create a File object.


Common Mistake #2: Forgetting the MIME Type

You can technically create:

const blob = new Blob([
  data
]);
Enter fullscreen mode Exit fullscreen mode

But if the consuming API needs to know what kind of data it is, specify the type:

const blob = new Blob(
  [data],
  {
    type: "application/pdf"
  }
);
Enter fullscreen mode Exit fullscreen mode

or:

const blob = new Blob(
  [data],
  {
    type: "image/png"
  }
);
Enter fullscreen mode Exit fullscreen mode

Common Mistake #3: Forgetting to Revoke Object URLs

Avoid repeatedly doing:

const url =
  URL.createObjectURL(blob);
Enter fullscreen mode Exit fullscreen mode

without eventually releasing the URL when appropriate.

Use:

URL.revokeObjectURL(url);
Enter fullscreen mode Exit fullscreen mode

Common Mistake #4: Manually Setting Content-Type with FormData

This is a common mistake:

fetch("/upload", {
  method: "POST",
  headers: {
    "Content-Type": "multipart/form-data"
  },
  body: formData
});
Enter fullscreen mode Exit fullscreen mode

Don't manually set this header in normal browser FormData uploads.

Use:

fetch("/upload", {
  method: "POST",
  body: formData
});
Enter fullscreen mode Exit fullscreen mode

The browser generates the correct multipart boundary.


Common Mistake #5: Assuming Blob Automatically Compresses Data

Blob does not mean:

compressed data
Enter fullscreen mode Exit fullscreen mode

A Blob is simply a representation of binary data.

For example:

new Blob([
  hugeString
]);
Enter fullscreen mode Exit fullscreen mode

doesn't automatically compress hugeString.

Compression is a separate concern.


Common Mistake #6: Converting Everything to Base64

Developers sometimes use:

Binary data → Base64 → JSON
Enter fullscreen mode Exit fullscreen mode

for everything.

This can be convenient in some APIs, but it isn't always the best choice.

Base64 introduces additional representation overhead and often makes binary transfer less direct.

For file operations, Blob/File plus fetch() or FormData is often a much more natural browser-side approach.


Blob in React

The Blob API isn't specific to vanilla JavaScript.

You can use it directly inside React.

Example:

function ImagePreview() {

  const [url, setUrl] =
    React.useState(null);

  const handleChange = event => {

    const file =
      event.target.files[0];

    if (!file) {
      return;
    }

    const objectUrl =
      URL.createObjectURL(file);

    setUrl(objectUrl);
  };

  return (
    <>
      <input
        type="file"
        accept="image/*"
        onChange={handleChange}
      />

      {url && (
        <img
          src={url}
          width="300"
          alt="Preview"
        />
      )}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

In a production React application, you should also clean up the object URL appropriately.


Blob in Angular

The same browser APIs are available in Angular.

For example:

this.http.get(
  '/api/report',
  {
    responseType: 'blob'
  }
);
Enter fullscreen mode Exit fullscreen mode

Then:

this.http.get(
  '/api/report',
  {
    responseType: 'blob'
  }
).subscribe(blob => {

  const url =
    URL.createObjectURL(blob);

  window.open(url);

});
Enter fullscreen mode Exit fullscreen mode

This is one of the most common Blob patterns in Angular applications.


A Complete API Download Example

Suppose the backend provides:

GET /api/reports/monthly
Enter fullscreen mode Exit fullscreen mode

and returns:

application/pdf
Enter fullscreen mode Exit fullscreen mode

The frontend can do:

async function downloadReport() {

  const response =
    await fetch(
      "/api/reports/monthly"
    );

  if (!response.ok) {
    throw new Error(
      "Failed to download report"
    );
  }

  const blob =
    await response.blob();

  const url =
    URL.createObjectURL(blob);

  const link =
    document.createElement("a");

  link.href = url;

  link.download =
    "monthly-report.pdf";

  document.body.appendChild(link);

  link.click();

  link.remove();

  URL.revokeObjectURL(url);
}
Enter fullscreen mode Exit fullscreen mode

This pattern is extremely common in real-world applications.


A Complete Upload + Preview Workflow

Let's put the concepts together.

flowchart TD
    A[User selects File] --> B[File object]
    B --> C[File is a Blob]
    C --> D[createObjectURL]
    D --> E[Image Preview]

    C --> F[FormData]
    F --> G[POST /upload]
    G --> H[Backend]

    H --> I[Stored File]

    H --> J[GET /download]
    J --> K[Response Blob]
    K --> L[Object URL]
    L --> M[Download]

This is the architecture behind many file upload/download interfaces.


When Should You Use Blob?

Blob is a good choice when you're dealing with browser-side binary or file-like data.

Use Blob when you need to:

Preview files

URL.createObjectURL(file)
Enter fullscreen mode Exit fullscreen mode

Download generated data

new Blob([...])
Enter fullscreen mode Exit fullscreen mode

Download API responses

response.blob()
Enter fullscreen mode Exit fullscreen mode

Upload binary data

fetch(url, {
  method: "POST",
  body: blob
});
Enter fullscreen mode Exit fullscreen mode

Generate files dynamically

new Blob([
  csv
]);
Enter fullscreen mode Exit fullscreen mode

Work with media

Images, audio, and video frequently interact with Blob/File APIs.


When Should You Use ArrayBuffer Instead?

Use ArrayBuffer when you need lower-level binary access.

For example:

const buffer =
  await response.arrayBuffer();
Enter fullscreen mode Exit fullscreen mode

This becomes more appropriate when you're manipulating the bytes themselves.

Examples include:

  • cryptography
  • binary protocols
  • binary file parsing
  • WebSockets
  • Web Audio
  • low-level encoding/decoding

A useful rule is:

Blob is file-oriented; ArrayBuffer is byte-oriented.

It's not an absolute rule, but it's a very useful mental model.


Blob Is Not Just "A File"

This is probably the most important idea to take away.

A Blob doesn't necessarily represent a physical file.

Instead, think of it as:

An immutable container for binary data that the browser can consume as file-like data.

That distinction explains why you can create a Blob from:

const blob = new Blob([
  "Hello"
]);
Enter fullscreen mode Exit fullscreen mode

without creating anything on the user's disk.

You can then decide what you want to do with it:

Blob
 ├── Preview
 ├── Download
 ├── Upload
 ├── Read as text
 ├── Read as ArrayBuffer
 ├── Stream
 └── Convert to File
Enter fullscreen mode Exit fullscreen mode

A Practical Mental Model

When working with browser file APIs, think about the following layers:

flowchart TD
    A[Data] --> B[Blob]
    B --> C[Object URL]
    C --> D[Browser Resource]

    B --> E[File]
    E --> F[File Upload]

    B --> G[ArrayBuffer]
    G --> H[Byte-Level Processing]

Each abstraction has a different job.

Blob

File-like binary data.

File

Blob + filename and file metadata.

Object URL

A temporary browser URL that points to Blob/File data.

ArrayBuffer

Raw binary memory.

Uint8Array

A byte-oriented view over binary memory.


Final Example: Everything Together

Here is a compact example that demonstrates several concepts at once.

<input
  type="file"
  id="fileInput"
/>

<img
  id="preview"
  width="300"
/>

<button id="download">
  Download
</button>
Enter fullscreen mode Exit fullscreen mode
const fileInput =
  document.getElementById("fileInput");

const preview =
  document.getElementById("preview");

const downloadButton =
  document.getElementById("download");

let selectedFile = null;
let previewUrl = null;

fileInput.addEventListener(
  "change",
  () => {

    const file =
      fileInput.files[0];

    if (!file) {
      return;
    }

    selectedFile = file;

    if (previewUrl) {
      URL.revokeObjectURL(previewUrl);
    }

    previewUrl =
      URL.createObjectURL(file);

    preview.src =
      previewUrl;
  }
);

downloadButton.addEventListener(
  "click",
  () => {

    if (!selectedFile) {
      return;
    }

    const url =
      URL.createObjectURL(
        selectedFile
      );

    const link =
      document.createElement("a");

    link.href = url;

    link.download =
      selectedFile.name;

    link.click();

    URL.revokeObjectURL(url);
  }
);
Enter fullscreen mode Exit fullscreen mode

The important flow is:

File
 ↓
Blob
 ↓
Object URL
 ↓
Preview
Enter fullscreen mode Exit fullscreen mode

and:

File
 ↓
Blob
 ↓
Object URL
 ↓
Download
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

The Blob API looks simple at first:

new Blob(...)
Enter fullscreen mode Exit fullscreen mode

but it is actually one of the fundamental browser APIs for working with binary data.

Once you understand Blob, several other browser concepts become much easier to understand:

  • File
  • FileReader
  • FormData
  • URL.createObjectURL()
  • fetch().blob()
  • ArrayBuffer
  • Uint8Array
  • file uploads
  • file downloads
  • image previews
  • generated files
  • API-generated PDFs
  • browser-side media processing

The most important mental model is:

Blob = immutable binary data
Enter fullscreen mode Exit fullscreen mode

Then:

File = Blob + file metadata
Enter fullscreen mode Exit fullscreen mode

and:

Object URL = temporary browser URL pointing to Blob/File data
Enter fullscreen mode Exit fullscreen mode

while:

ArrayBuffer = raw binary memory
Enter fullscreen mode Exit fullscreen mode

and:

Uint8Array = byte-level view of that memory
Enter fullscreen mode Exit fullscreen mode

Once these distinctions are clear, working with files and binary data in JavaScript becomes much less mysterious.


Quick Cheat Sheet

// Create Blob
const blob = new Blob(
  ["Hello"],
  {
    type: "text/plain"
  }
);

// Size
console.log(blob.size);

// MIME type
console.log(blob.type);

// Read as text
const text = await blob.text();

// Read as binary memory
const buffer =
  await blob.arrayBuffer();

// Create temporary URL
const url =
  URL.createObjectURL(blob);

// Release temporary URL
URL.revokeObjectURL(url);

// Convert Blob to File
const file = new File(
  [blob],
  "hello.txt",
  {
    type: blob.type
  }
);

// Get Blob from Fetch response
const response =
  await fetch("/file");

const downloadedBlob =
  await response.blob();
Enter fullscreen mode Exit fullscreen mode

If you understand these APIs, you already have the foundation needed for most browser-side file and binary-data workflows.

Top comments (0)