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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To preview an image before upload, let the user select it with <input type="file">, then set an <img> element’s source to a temporary object URL made with URL.createObjectURL(file). CSS controls the preview box and whether the image is shown whole or cropped. Revoke each object URL when its preview is replaced or removed.

Build a working image preview

This example uses a real file input, a connected label, a status message, and an image preview. It displays the first selected image, clears the previous preview before handling a new selection, and releases the temporary object URL when it is no longer needed.

HTML

<label class="file-picker" for="image-input">Choose an image</label>
<input id="image-input" type="file" accept="image/*">

<div class="preview-frame" aria-live="polite">
  <img id="preview" alt="Selected image preview" hidden>
  <p id="status">No image selected.</p>
</div>

CSS

.file-picker {
  display: inline-block;
  padding: .6rem 1rem;
  border: 1px solid #666;
  border-radius: .4rem;
  cursor: pointer;
}

/* Visually reduce the native input without removing it from interaction. */
#image-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.file-picker:focus-visible {
  outline: 2px solid #155eef;
  outline-offset: 2px;
}

.preview-frame {
  width: min(100%, 32rem);
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: #f3f4f6;
  border: 1px dashed #9ca3af;
}

#preview {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

JavaScript

const input = document.querySelector('#image-input');
const image = document.querySelector('#preview');
const status = document.querySelector('#status');
let previewURL = null;

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

  if (previewURL) {
    URL.revokeObjectURL(previewURL);
    previewURL = null;
  }

  image.hidden = true;
  image.removeAttribute('src');

  if (!file) {
    status.textContent = 'No image selected.';
    return;
  }

  if (!file.type.startsWith('image/')) {
    status.textContent = 'Choose an image file.';
    return;
  }

  previewURL = URL.createObjectURL(file);
  image.src = previewURL;
  image.hidden = false;
  status.textContent = `${file.name} selected.`;
});

window.addEventListener('pagehide', () => {
  if (previewURL) URL.revokeObjectURL(previewURL);
});

Put the HTML in the page, the CSS in a stylesheet or <style> block, and the JavaScript after the elements or in a deferred script. When the user chooses a file, the browser exposes it through input.files; the script creates a local object URL and assigns it to the image. The file is not uploaded by this code.

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

Why use an object URL?

URL.createObjectURL(file) creates a string URL that identifies the selected File, so an image element can display it directly. This avoids converting the entire file into a base64 data URL. It is useful for a local preview because it does not require sending the image to a server first.

#1 Best Overall
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Each object-URL creation returns a unique URL. Keep track of the current one and call URL.revokeObjectURL() when that preview is replaced or removed. The example revokes the previous URL at the start of the next selection and the remaining URL when the page is left. Do not revoke it immediately on the image’s load event if the user still needs to view or interact with that preview.

Choose how the preview fits its frame

The frame’s aspect ratio and the image’s object-fit setting determine its presentation. With the example’s aspect-ratio: 4 / 3, differently shaped images are fitted into a landscape preview area.

CSS choice What the reader sees Use it when
object-fit: contain The whole image remains visible; unused space may appear around it. The user needs to inspect the complete image without cropping.
object-fit: cover The frame is filled, but parts of the image outside the frame may be cropped. A consistently filled thumbnail matters more than showing every edge.

For a cover crop, set object-position to adjust which part stays in view—for example, object-position: center top aligns the crop toward the top. Changing the frame’s width or aspect ratio changes the available preview shape; it does not alter the selected file.

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

Keep file selection accessible and validate it

The accept="image/*" attribute hints to the file picker that images are wanted. It is not validation: retain the runtime type check, and validate again on the server when the file is uploaded. A client-side preview is not a security boundary.

  • Associate the visible label with the input using matching for and id values.
  • Keep the input available to keyboard and assistive-technology users. Avoid hiding it with display: none or visibility: hidden if that removes it from interaction.
  • Provide a visible keyboard focus cue. If the input is visually hidden, ensure the focus indicator is still visible on a related control or wrapper.
  • Clear the previous image and update nearby status text when there is no selection or the selected file is not an image.

The example’s aria-live="polite" region provides a place for status changes to be announced. For a production form, make sure the actual file input remains keyboard operable and that its focus state is perceivable; test the label and focus behavior with the assistive technologies your users rely on.

Show several selected images

For a multi-image picker, add the multiple attribute and iterate over input.files instead of reading only the first item. Create one preview element and one object URL per accepted file, and keep the URLs in a collection so they can all be revoked when the previews are cleared or replaced.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
input.addEventListener('change', () => {
  clearPreviews();

  for (const file of input.files ?? []) {
    if (!file.type.startsWith('image/')) continue;

    const url = URL.createObjectURL(file);
    previewURLs.push(url);

    const image = document.createElement('img');
    image.src = url;
    image.alt = `Preview of ${file.name}`;
    image.className = 'thumbnail';
    previewList.append(image);
  }
});

function clearPreviews() {
  for (const url of previewURLs) URL.revokeObjectURL(url);
  previewURLs = [];
  previewList.replaceChildren();
}

This snippet assumes previewURLs is initialized as an array and previewList refers to a container in the page. Add multiple to the input and a corresponding container, for example <input id="image-input" type="file" accept="image/*" multiple>. The same object-URL lifecycle applies: removing image elements alone does not revoke their URLs.

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.

When FileReader is a better fit

Use FileReader if the rest of your workflow specifically needs a data URL string—for example, to pass that representation to code expecting one. It reads the selected file asynchronously; assign reader.result after the reader’s load event.

const reader = new FileReader();

reader.addEventListener('load', () => {
  image.src = reader.result;
  image.hidden = false;
});

reader.readAsDataURL(file);

The data-URL approach is an alternative to an object URL, not a prerequisite for displaying a local image. If you use it, handle read failures and clear stale previews just as you would in the object-URL version.

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

Troubleshoot a blank or incorrect preview

  • The preview stays blank: Check that the script selects the right element IDs and runs after the HTML exists. Inspect the browser console for errors, and confirm that the change handler receives a file.
  • The status says to choose an image: The selected file’s reported MIME type does not begin with image/. The picker’s accept hint is not a substitute for the check. If your application supports unusual image formats, define and validate the formats it actually supports rather than accepting arbitrary files.
  • A previous image remains visible: Hide the image and remove its old src before returning from an empty or invalid selection path. The sample does this before validation.
  • The image looks cropped: Change object-fit: cover to contain, or adjust object-position if cropping is intentional.
  • The picker cannot be reached or focus is hard to see: Do not remove the input from keyboard interaction with display: none or visibility: hidden. Check that the label is connected and provide a visible focus cue.
  • Memory or resources accumulate after repeated selections: Revoke each old object URL when you replace or remove that preview. For a set of images, retain and release every URL rather than only the most recent one.
  • A preview appears locally but the upload fails: Previewing and uploading are separate operations. The example only reads a user-selected file in the browser; implement the form submission or upload request separately and validate the received file on the server.

Or skip the browser setup

This is a different job from previewing a file someone just selected: ScreenshotNeo captures a remote webpage, not a local file-input selection. If you need a screenshot of a page—such as a page containing your own preview component—one GET request returns an image or PDF. See the ScreenshotNeo website and API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Replace YOUR_API_KEY with your key and change the target URL to the webpage you want to capture. ScreenshotNeo accepts and removes cookie or consent banners, 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, and responses identify the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots.

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.

Sign up for ScreenshotNeo’s free plan to try capturing webpages.

Frequently Asked Questions

Does the image preview upload the file?

No. The object URL displays the file selected in the browser; upload requires a separate form submission or request.

Can I preview an image dragged onto the page?

Yes. Read the dropped files from the drop event’s file collection and apply the same validation and object-URL cleanup used for a file input.

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.

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