Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use fetch(), check the HTTP status, and then await response.blob():

async function imageUrlToBlob(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Image request failed: ${response.status}`);
  }
  return response.blob();
}

const blob = await imageUrlToBlob(imageUrl);

This works when the image is same-origin or its server permits your page through CORS. If you need to display the result through an API that expects a URL, create a temporary object URL and revoke it when you are finished.

The basic image-URL-to-Blob pattern

fetch(url) returns a Response. Checking response.ok prevents an HTTP error page from being treated as an image. Calling response.blob() then reads the response body to completion and resolves with a Blob; its media type comes from the response’s Content-Type header. See MDN’s fetch() documentation and Response.blob().

async function imageUrlToBlob(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`Image request failed: ${response.status} ${response.statusText}`);
  }

  return await response.blob();
}

const blob = await imageUrlToBlob("https://example.com/photo.jpg");
console.log(blob.size, blob.type);

The returned object contains the downloaded bytes and a MIME type such as image/jpeg or image/png. The operation does not decode and re-encode the picture; it exposes the response body as a Blob.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When to use a Blob—and when not to

Goal Best approach What it gives JavaScript
Show an image only <img src="https://example.com/photo.jpg"> The browser can render the remote resource, but your script does not automatically receive its bytes.
Upload, inspect, or process bytes fetch() followed by response.blob() A Blob that can be passed to file-processing code or an upload API.
Give an image element a local temporary URL URL.createObjectURL(blob) A blob: URL that can be assigned to src.

Displaying a cross-origin image and reading that image in JavaScript are separate permissions. A remote URL may work in an <img> while a fetch of the same URL is rejected by the browser.

Cross-origin images and CORS

For a cross-origin URL, browser Fetch uses CORS. The image server must return an appropriate Access-Control-Allow-Origin header authorizing the origin of your page. If it does not, the browser blocks script access to the response. The browser’s CORS model is described in MDN’s CORS guide.

Do not use mode: "no-cors" as a workaround

no-cors produces an opaque response. Its body and headers are unavailable to JavaScript; calling blob() on that response produces a zero-byte Blob with an empty type, which is not a usable image Blob. MDN’s Fetch API guide documents this behavior.

// This does not make a foreign image readable:
const response = await fetch(foreignUrl, { mode: "no-cors" });
const unusable = await response.blob();
console.log(unusable.size, unusable.type); // 0, ""

What to do when CORS is missing

  • Configure the image host to allow your web origin with Access-Control-Allow-Origin.
  • Fetch the image through a server-side endpoint that you control, while respecting the remote host’s access rules and terms.
  • If you only need visual display, keep the URL directly on an <img> and do not attempt to read its bytes in browser JavaScript.

There is no client-side flag that legitimately bypasses this browser security boundary.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A production-ready helper

For application code, add a timeout, preserve the original URL in errors, and optionally reject responses that are not images. A successful HTTP status alone does not prove that the body is an image: a server can return an HTML error document with status 200.

async function imageUrlToBlob(url, options = {}) {
  const {
    timeoutMs = 30_000,
    requireImage = true,
    signal: callerSignal
  } = options;

  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  // Forward cancellation from the caller as well as the timeout.
  const abortFromCaller = () => controller.abort();
  callerSignal?.addEventListener("abort", abortFromCaller, { once: true });

  try {
    const response = await fetch(url, { signal: controller.signal });

    if (!response.ok) {
      throw new Error(`Image request failed (${response.status}) for ${url}`);
    }

    const blob = await response.blob();
    if (requireImage && !blob.type.toLowerCase().startsWith("image/")) {
      throw new Error(`Expected an image but received ${blob.type || "an unknown type"}`);
    }

    return blob;
  } catch (error) {
    if (error.name === "AbortError") {
      throw new Error(`Image request timed out or was cancelled: ${url}`);
    }
    throw error;
  } finally {
    clearTimeout(timer);
    callerSignal?.removeEventListener("abort", abortFromCaller);
  }
}

The timeout is an application policy rather than a Fetch requirement. Choose a value appropriate for your users and image sizes. If you do not need type validation, pass { requireImage: false }; the Blob’s type still reflects the response header.

Displaying a Blob with an object URL

Use the Blob directly whenever the next API accepts a Blob. Create an object URL only when an API, such as an image element, requires a URL:

const blob = await imageUrlToBlob("https://example.com/photo.jpg");
const image = document.querySelector("#preview");
const objectUrl = URL.createObjectURL(blob);

image.src = objectUrl;
image.addEventListener("load", () => {
  console.log("Image is ready");
}, { once: true });

// Keep objectUrl while the image is displayed or the user may save it.
function disposePreview() {
  URL.revokeObjectURL(objectUrl);
  image.removeAttribute("src");
}

Every URL.createObjectURL() call creates a new temporary URL. Release it with URL.revokeObjectURL() after the resource is no longer accessible or needed. Revoking immediately after load can make a preview unavailable before a user can interact with or save it. See MDN’s blob: URL reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common ways to use the Blob

Upload it with FormData

const blob = await imageUrlToBlob(imageUrl);
const form = new FormData();
form.append("image", blob, "downloaded-image");

const uploadResponse = await fetch("/api/images", {
  method: "POST",
  body: form
});
if (!uploadResponse.ok) {
  throw new Error(`Upload failed: ${uploadResponse.status}`);
}

Turn it into a File

Some libraries require a File rather than a generic Blob. Preserve the Blob’s media type:

const blob = await imageUrlToBlob(imageUrl);
const extension = blob.type === "image/png" ? "png" : "bin";
const file = new File([blob], `image.${extension}`, {
  type: blob.type || "application/octet-stream",
  lastModified: Date.now()
});

Download it in the browser

const blob = await imageUrlToBlob(imageUrl);
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = objectUrl;
link.download = "image";
link.click();
URL.revokeObjectURL(objectUrl);

If a user must interact with the downloaded resource through a visible page, delay revocation until that interaction is complete; for a programmatic download, revoke after triggering the download according to your browser support requirements.

Troubleshooting fetch failures

Symptom Likely cause Fix
TypeError: Failed to fetch or a CORS message The remote server did not authorize your page’s origin, or the request failed before a response was exposed. Enable the correct CORS response header on the image host, use a server-side fetch under your control, or use a direct <img> when bytes are unnecessary.
Blob has size 0 and an empty type An opaque response was created, commonly by mode: "no-cors". Remove no-cors and arrange proper CORS permission; the opaque body cannot be recovered in JavaScript.
Non-image type such as text/html The URL returned an error page, login page, or other document despite an HTTP success status. Check the URL, authentication, redirects, and the response Content-Type; reject unexpected types before processing.
HTTP 404, 403, or 500 The image request itself failed. Check response.ok, report the status, and correct the URL or server permissions instead of calling blob() blindly.
Preview disappears or cannot be saved The object URL was revoked too early. Keep the URL alive until the image is no longer visible or usable, then call URL.revokeObjectURL().
Request never finishes The connection or server is stalled. Use an AbortController timeout, as in the helper above, and provide a retry or error state appropriate to your application.

Performance and reliability considerations

  • response.blob() consumes the response stream to completion, so memory usage includes the downloaded bytes and any object or decoded image data your page creates.
  • Do not create an object URL when a Blob can be passed directly to the next API; this avoids an unnecessary lifecycle to manage.
  • Validate status before consuming the body so large error documents are not processed as images.
  • Release object URLs when previews, editors, or cached records are discarded. A long-lived page that creates many URLs without revoking them can retain resources.
  • For large or unreliable downloads, combine a timeout with visible progress or cancellation behavior. Fetch’s simple blob() promise resolves only after the body has been read completely.

Or skip the browser setup

If your actual goal is to obtain a clean screenshot of a web page rather than fetch an existing image file, ScreenshotNeo returns PNG, JPEG, WebP, or PDF from one request. It accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and lets you turn those steps off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; the response identifies the result with X-Page-Verdict and X-Billed headers.

For a screenshot that you then want as a Blob in browser code, call the API from your server or another permitted environment and treat its binary response like any other image response. The API documentation is at screenshotneo.com/docs/.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Features include full-page capture with lazy images loaded, CSS-selector element capture, device presets and custom viewports, retina scale, custom CSS and JavaScript, click and wait actions, request blocking, cookies and headers, timezone and geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs.

The Free plan includes 1,000 screenshots each month without a card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan. Create a free ScreenshotNeo account to get started.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

Does converting an image URL to a Blob change the image?

No. The operation exposes the response body bytes and reported media type; it does not perform image editing or format conversion. Any resizing, decoding, or re-encoding must be a separate step.

Can I read a Blob’s MIME type reliably?

You can inspect blob.type, which is derived from the response’s Content-Type. Treat it as an input to validation, not proof that the bytes are valid image data; a server can send the wrong type or an HTML document.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does an image render in HTML but fail in JavaScript?

Rendering through <img> does not grant script access to cross-origin bytes. A fetch requires the image server’s CORS permission, so the two operations can have different results.

Frequently Asked Questions

Does converting an image URL to a Blob change the image?

No. It exposes the response body bytes and reported media type; image editing or format conversion is a separate operation.

Can I read a Blob’s MIME type reliably?

Inspect blob.type, which comes from the response’s Content-Type. Validate the actual content as needed because a server can return the wrong type or an HTML document.

Why can an image render in HTML but fail in JavaScript?

An <img> may display a cross-origin resource while Fetch still blocks JavaScript from reading its bytes unless the image server allows your origin through CORS.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.