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

To preview an image before uploading it, read the selected File from a file input, create a temporary URL with URL.createObjectURL(), and assign that URL to an <img> element. The browser displays the local file; this pattern does not send it to your server. Revoke each object URL when its preview is no longer needed.

Preview one selected image with an object URL

Start with a file input and an image element. The accept attribute guides the file picker toward images, while JavaScript checks the selected file before displaying it. Neither is a substitute for validating an upload on the server.

<label for="image-input">Choose an image</label>
<input id="image-input" type="file" accept="image/*">
<p id="image-error" role="status"></p>
<img id="image-preview" alt="Selected image preview" hidden>

Place this script after the markup, or load it with defer so the elements exist when it runs:

const input = document.querySelector("#image-input");
const preview = document.querySelector("#image-preview");
const error = document.querySelector("#image-error");
let previewUrl;

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

  // Release the previous selection's URL before replacing it.
  if (previewUrl) {
    URL.revokeObjectURL(previewUrl);
    previewUrl = undefined;
  }

  preview.removeAttribute("src");
  preview.hidden = true;
  error.textContent = "";

  if (!file) {
    return;
  }

  if (!file.type.startsWith("image/")) {
    error.textContent = "Choose an image file to see a preview.";
    return;
  }

  previewUrl = URL.createObjectURL(file);
  preview.src = previewUrl;
  preview.hidden = false;
});

function clearImagePreview() {
  if (previewUrl) {
    URL.revokeObjectURL(previewUrl);
    previewUrl = undefined;
  }
  preview.removeAttribute("src");
  preview.hidden = true;
  error.textContent = "";
  input.value = "";
}

The input’s files property provides the selected File, and a File can be passed to URL.createObjectURL() to make a URL usable as an image source. See MDN’s Using files from web applications and File references.

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

What the code does

  • input.files?.[0] gets the first selected file. If the user cancels the picker or clears the selection, the handler returns without showing an image.
  • file.type.startsWith("image/") filters files based on their reported MIME type. The accept="image/*" attribute also guides the picker, but it is a user-interface hint rather than proof of a file’s contents. MDN documents the input attribute and examples of type checks in its file input reference.
  • URL.createObjectURL(file) creates a temporary URL that points to the selected file; it does not encode the whole image into a JavaScript string.
  • URL.revokeObjectURL(previewUrl) releases the object URL when replacing or clearing the preview. MDN notes that each call to createObjectURL() creates a new URL, including when called again for the same object. See blob: URLs and URL.revokeObjectURL().

Keep the preview usable, then clean it up

Do not revoke an object URL as soon as the image fires its load event if the image remains on screen. The preview may still need to support interactions such as opening the image in a new tab or saving it. MDN explicitly cautions against immediate revocation for that reason in its object URL example.

Instead, keep the URL while that preview is in use. Revoke it when the user selects a replacement, clears the preview, or the component that owns the preview is removed. For a component-based interface, put the cleanup in the component’s unmount or teardown lifecycle and remove the image source as part of that cleanup. The example’s clearImagePreview() function handles a user-initiated clear; call it from your clear button or component cleanup.

Leaving old object URLs untracked makes cleanup difficult, especially if users repeatedly choose different files. Store the current URL, or store all URLs if you show multiple files, and revoke each one when its image is no longer needed. The browser API reference describes revocation in URL.revokeObjectURL().

Preview several images

For multiple previews, add multiple to the input and create one image and one object URL for every accepted file. Keep the URLs in a collection so you can revoke all of them when the selection is replaced or cleared.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<label for="images-input">Choose images</label>
<input id="images-input" type="file" accept="image/*" multiple>
<p id="images-error" role="status"></p>
<div id="image-list"></div>
const imagesInput = document.querySelector("#images-input");
const imageList = document.querySelector("#image-list");
const imagesError = document.querySelector("#images-error");
const previewUrls = [];

function clearImageList() {
  for (const url of previewUrls) {
    URL.revokeObjectURL(url);
  }
  previewUrls.length = 0;
  imageList.replaceChildren();
}

imagesInput.addEventListener("change", () => {
  clearImageList();
  imagesError.textContent = "";

  const files = Array.from(imagesInput.files ?? []);
  const imageFiles = files.filter(file => file.type.startsWith("image/"));

  if (files.length === 0) {
    return;
  }

  if (imageFiles.length !== files.length) {
    imagesError.textContent = "Some selected files were not identified as images and were skipped.";
  }

  for (const file of imageFiles) {
    const url = URL.createObjectURL(file);
    previewUrls.push(url);

    const image = document.createElement("img");
    image.src = url;
    image.alt = `Preview of ${file.name}`;
    imageList.append(image);
  }
});

function removeImageList() {
  clearImageList();
  imagesInput.value = "";
  imagesError.textContent = "";
}

This makes one preview for each selected file, skips files whose reported MIME type does not start with image/, and gives the user feedback when the selection contains rejected files. MDN’s file input guidance includes a multiple-file thumbnail example and invalid-type feedback; its file API guide covers working with selected files.

Use the same preview logic for drag and drop

A drop target changes how files enter the interface, not how local image previews work. Read files from the drop event, filter them, then pass the accepted File objects to the same rendering function used for picker selections. Prevent the browser’s default drop behavior on the target so that dropping a file does not navigate away from the page.

For example, refactor the multi-image code so a function accepts a file array:

function showImages(files) {
  clearImageList();
  imagesError.textContent = "";

  const imageFiles = files.filter(file => file.type.startsWith("image/"));
  if (imageFiles.length !== files.length) {
    imagesError.textContent = "Some files were skipped because they were not identified as images.";
  }

  for (const file of imageFiles) {
    const url = URL.createObjectURL(file);
    previewUrls.push(url);
    const image = document.createElement("img");
    image.src = url;
    image.alt = `Preview of ${file.name}`;
    imageList.append(image);
  }
}

imagesInput.addEventListener("change", () => {
  showImages(Array.from(imagesInput.files ?? []));
});

const dropZone = document.querySelector("#drop-zone");
dropZone.addEventListener("dragover", event => event.preventDefault());
dropZone.addEventListener("drop", event => {
  event.preventDefault();
  showImages(Array.from(event.dataTransfer?.files ?? []));
});

Add a corresponding element such as <div id="drop-zone">Drop images here</div> to the page. The rendering function clears the prior list and revokes its URLs before creating new ones. MDN’s drag-and-drop example also demonstrates revoking image URLs when a preview list is cleared.

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

Object URLs versus FileReader data URLs

For the direct image-preview pattern, an object URL is usually the straightforward choice: create a URL for the File, set it as src, and revoke it during cleanup. A second approach is FileReader, which reads file contents and can produce a data URL for the image source.

Approach What becomes the image source Cleanup and trade-off
Object URL A temporary URL referencing the selected file Track and revoke each URL when finished. MDN says blob URLs reference the underlying object.
FileReader data URL A data URL containing the file’s encoded contents The resource is embedded in the URL; MDN notes data URLs have severe size limitations.

MDN’s blob: URL reference discusses the difference between blob URLs and data URLs. Unless you specifically need file contents represented as a data URL, the object URL pattern avoids building that representation just to display a local image.

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

Troubleshoot common preview problems

The preview does not appear

  • Confirm the input and image selectors match the IDs in your HTML. If the script runs before the markup is parsed, move it below the elements or load it with defer.
  • Check that a file was selected and that the code sets the image’s src to the URL returned by URL.createObjectURL(file).
  • Inspect the selected file’s type. If it is empty or does not begin with image/, the example intentionally rejects it. The MIME type is a UI filter, not proof that the contents are safe or valid.

The old image remains after a new selection or clear

Remove the old src and hide the element when there is no valid selection. Revoke the previous URL before replacing it, and make sure the clear action resets the input if you want selecting the same file again to trigger a new change event.

The image works initially but stops supporting interactions

Check whether the URL is revoked in the image’s load handler. Keep it active for as long as the displayed preview should remain usable; revoke it when the preview is replaced or removed, as MDN advises in its object URL example.

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

Memory use grows during repeated selections

Track every URL returned by createObjectURL() and revoke it when its preview is discarded. A multiple-image interface must clean up every URL, not just the most recently created one. The revokeObjectURL reference documents the cleanup method.

A non-image or unexpected file is selected

Use accept="image/*" to guide the picker and show a clear message when your JavaScript filter rejects a file. The browser’s reported MIME type alone does not establish that a file’s contents are actually an image. Validate uploads on the server as well; a client-side preview is not an upload security check.

Or skip the browser setup

ScreenshotNeo is a website screenshot API, not a local-file preview library; use the browser code above when the goal is to show a user’s selected file before upload. If you instead need a screenshot of a web page, ScreenshotNeo takes a URL and returns an image or PDF. One cURL request looks like this; see the ScreenshotNeo documentation for API details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
  • It accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off.
  • Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing status in headers.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.

Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month with no card.

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.