Skip to content
FLAVIO COPES
flaviocopes.com
2026

The Blob Object

By

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

~~~

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:

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:

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:

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

Read the bytes in an ArrayBuffer:

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

blob.bytes() returns a Uint8Array directly:

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:

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:

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:

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:

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:

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:

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:

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:

<input id="avatar" type="file" accept="image/*">
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, the Blob() constructor, and URL.createObjectURL().

Tagged: Web Platform ยท All topics
~~~

Related posts about platform: