# The Blob Object

> Learn how browser Blob objects hold immutable binary data, and how to create, read, download, upload, slice, and release them safely.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2019-04-27 | Updated: 2026-07-18 | Topics: [Web Platform](https://flaviocopes.com/tags/platform/) | Canonical: https://flaviocopes.com/blob/

A `Blob` represents immutable raw data in the browser. The data can be text or binary, and it does not need to fit a JavaScript string.

You will encounter blobs when working with files, generated downloads, images, audio, fetch responses, workers, and IndexedDB.

`File` inherits from `Blob`, so the same reading and slicing methods also work on files selected by a user.

## Create a Blob

Pass an iterable of data parts to the `Blob()` constructor:

```js
const blob = new Blob(
  ['Hello from a Blob\n'],
  { type: 'text/plain;charset=utf-8' },
)
```

Each part can be a string, another `Blob`, an `ArrayBuffer`, a typed array, or a `DataView`.

The `type` option is a MIME type. It is metadata supplied by the code creating the blob, not a guarantee that the bytes actually have that format.

A blob has two useful properties:

```js
console.log(blob.size)
console.log(blob.type)
```

`size` is the number of bytes. `type` is the MIME type, or an empty string when no type is known.

## Read a Blob

Modern Blob objects have promise-based reading methods.

Read text:

```js
const text = await blob.text()
console.log(text)
```

Read the bytes in an `ArrayBuffer`:

```js
const buffer = await blob.arrayBuffer()
const bytes = new Uint8Array(buffer)
```

`blob.bytes()` returns a `Uint8Array` directly:

```js
const bytes = await blob.bytes()
```

`bytes()` is newer than `arrayBuffer()`, so check your browser support requirements before using it.

For large data, `stream()` exposes a `ReadableStream` and avoids waiting for the entire blob to be read first:

```js
const stream = blob.stream()
```

You usually do not need `FileReader` for these operations. It remains useful for older event-based code and for reading data as a data URL.

## Slice a Blob

`slice()` creates a new blob that references part of the original data:

```js
const firstFiveBytes = blob.slice(0, 5, 'text/plain')
console.log(await firstFiveBytes.text())
```

The start position is included and the end position is excluded. Negative positions count backward from the end.

The original blob is unchanged.

## Get a Blob from fetch

Call `response.blob()` when a server response contains file or image data:

```js
const response = await fetch('/images/avatar.png')

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`)
}

const imageBlob = await response.blob()
```

The response body can only be consumed once. Do not call both `response.json()` and `response.blob()` on the same response unless you first clone it for a specific reason.

## Display a Blob with an object URL

`URL.createObjectURL()` creates a temporary `blob:` URL:

```js
const url = URL.createObjectURL(imageBlob)
const image = document.querySelector('img')

image.src = url
image.addEventListener('load', () => {
  URL.revokeObjectURL(url)
}, { once: true })
```

The browser keeps the blob available while the object URL exists. Call `URL.revokeObjectURL()` when the URL is no longer needed to release that reference.

Do not revoke an image URL immediately after assigning `src`; wait until the resource has loaded or the user can no longer interact with it.

## Download generated data

An object URL can also power a download:

```js
const report = new Blob(
  [JSON.stringify({ status: 'ok' }, null, 2)],
  { type: 'application/json' },
)

const url = URL.createObjectURL(report)
const link = document.createElement('a')

link.href = url
link.download = 'report.json'
link.click()

URL.revokeObjectURL(url)
```

For a download triggered by a visible link, revoke the URL after the user no longer needs the link.

## Upload a Blob

You can send a blob directly as a fetch request body:

```js
const response = await fetch('/upload', {
  method: 'POST',
  headers: {
    'content-type': blob.type || 'application/octet-stream',
  },
  body: blob,
})
```

To send a file together with form fields, use `FormData`:

```js
const formData = new FormData()
formData.append('avatar', imageBlob, 'avatar.png')

await fetch('/profile/avatar', {
  method: 'POST',
  body: formData,
})
```

Do not set the `content-type` header yourself when sending `FormData`. The browser adds the required multipart boundary.

Use `XMLHttpRequest` instead of fetch only when you specifically need its upload progress events.

## Get a File from an input

A file selected with `<input type="file">` is a `File`, and therefore also a `Blob`:

```html
<input id="avatar" type="file" accept="image/*">
```

```js
const input = document.querySelector('#avatar')

input.addEventListener('change', async () => {
  const file = input.files[0]
  if (!file) return

  console.log(file.name)
  console.log(file.size)
  console.log(await file.arrayBuffer())
})
```

The `accept` attribute guides the file picker. It is not validation. A server must still validate uploaded bytes, size, and content.

See the MDN references for [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob), the [`Blob()` constructor](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob), and [`URL.createObjectURL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static).
