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

Use a file input to get the selected File, create a temporary browser URL with URL.createObjectURL(file), and render that URL in an <img>. This displays the image locally; it does not upload, validate on the server, or store the file. Keep the original File for the separate upload step, and revoke each object URL when its preview is replaced or removed.

Preview an image in React with an object URL

For a temporary preview, an object URL is a direct way to let an image element display a browser-selected file. The file stays in the browser until your app separately submits it. The following component handles selection, replacement, clearing, preview rendering, and URL cleanup.

import { useEffect, useState } from 'react';

export default function ImagePicker() {
  const [file, setFile] = useState(null);
  const [previewUrl, setPreviewUrl] = useState(null);

  function handleChange(event) {
    const nextFile = event.target.files?.[0] ?? null;
    setFile(nextFile);
    setPreviewUrl(nextFile ? URL.createObjectURL(nextFile) : null);
  }

  function clearSelection() {
    setFile(null);
    setPreviewUrl(null);
  }

  useEffect(() => {
    return () => {
      if (previewUrl) URL.revokeObjectURL(previewUrl);
    };
  }, [previewUrl]);

  return (
    <div>
      <label>
        Choose an image
        <input
          type="file"
          accept="image/*"
          onChange={handleChange}
        />
      </label>

      {previewUrl && (
        <div>
          <img src={previewUrl} alt="Preview of selected image" />
          <button type="button" onClick={clearSelection}>
            Remove image
          </button>
        </div>
      )}

      {file && <p>Selected: {file.name}</p>}
    </div>
  );
}

In JSX, onChange runs when the selection changes. The first selected file is available at event.target.files[0]; optional chaining and the null fallback handle an empty file list. URL.createObjectURL returns a temporary URL suitable for the image source. The conditional render ensures React does not create an image with an empty src.

What happens when the image is replaced or cleared

When previewUrl changes, React runs the previous effect cleanup before setting up the effect for the new value. That cleanup revokes the previous URL. When the component unmounts, it revokes the remaining URL. The remove button clears both the selected file and preview; the input’s change handler also clears them if the user clears the picker and triggers a change.

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

Do not revoke the URL immediately after the image’s load event if a user still needs to interact with the displayed image. The preview should remain usable for as long as the UI needs it. Also avoid calling createObjectURL during render: React can render more than once, and render-time creation can leave URLs without a matching cleanup.

Submit the original File separately

For an upload form, use the File stored in state—not the preview URL. For example, a submit handler can create a FormData payload and send it to your own upload endpoint:

async function uploadImage(file) {
  const formData = new FormData();
  formData.append('image', file);

  const response = await fetch('/api/upload', {
    method: 'POST',
    body: formData,
  });

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

  return response.json();
}

Call uploadImage(file) from your form’s submit handler after checking that a file is selected. Replace /api/upload with the endpoint your application actually implements; the example does not define a server or storage service. Do not manually set the multipart Content-Type header for this FormData request, since the browser supplies the required boundary.

Use FileReader when you need a data URL

FileReader.readAsDataURL(file) asynchronously reads a File or Blob and produces a data: URL when reading completes. Use this route when another part of your application specifically needs the encoded data URL; it is not a required step for an ordinary preview.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function readAsDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(file);
  });
}

// In an async change handler:
const nextFile = event.target.files?.[0] ?? null;
setFile(nextFile);
setPreviewUrl(nextFile ? await readAsDataUrl(nextFile) : null);

The handler using await must be declared async. Consider what should happen if a user selects another file before the first read finishes: an older asynchronous result must not overwrite the newer selection. A simple approach is to assign each read a sequence number or use a cancellation/identity check before updating state. With object URLs, there is no asynchronous file-reading result to race against a later selection.

Consideration Object URL Data URL via FileReader
Preview source A temporary browser URL referencing the file A URL string containing the file data
Lifecycle Revoke with URL.revokeObjectURL when the preview is no longer needed Wait for the asynchronous read to finish; retain or discard the resulting string as needed
Best fit Temporary local display An application flow that specifically needs a data URL representation

These approaches produce different representations and cleanup concerns. No performance advantage is assumed here; choose according to what the application needs, and measure if performance is important to your use case.

Validate the selection; do not treat accept as security

accept="image/*" guides the file picker toward image files. It is a convenience for selection, not a security boundary. A user or client can still provide unexpected content, and the browser preview does not prove that a server will accept the file.

  • Check the file size and permitted formats against your product requirements before attempting upload.
  • Validate the uploaded content on the server as part of the upload design. Do not rely only on the filename, extension, or picker hint.
  • Show a useful error and retain or clear the previous preview intentionally when a selection fails your client-side checks.
  • Keep upload authorization, storage rules, and server-side validation separate from preview rendering.

The exact limits and accepted formats are application-specific; set them to match your backend and product policy rather than assuming that every image type is supported by your service.

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

Handle multiple images with one URL per file

If users can select several images, add multiple to the file input and create a separate object URL for each file. Keep the file and its URL together so the preview can be rendered and every URL can be revoked when that item is removed or the component unmounts.

const [items, setItems] = useState([]);

function handleMultipleChange(event) {
  const files = Array.from(event.target.files ?? []);
  const nextItems = files.map((file) => ({
    file,
    previewUrl: URL.createObjectURL(file),
  }));
  setItems(nextItems);
}

useEffect(() => {
  return () => {
    items.forEach(({ previewUrl }) => URL.revokeObjectURL(previewUrl));
  };
}, [items]);

This abbreviated pattern cleans up the URLs when the item list changes, so replacing the entire list releases the previous list’s URLs. For per-item removal or incremental additions, use a lifecycle strategy that revokes only the removed item’s URL and also releases all remaining URLs on unmount. Do not regenerate URLs for existing files on every render.

Troubleshoot common preview problems

  • No preview appears: Confirm that the change handler reads event.target.files?.[0], that a file was selected, and that the image element is rendered only when the URL exists.
  • The image is broken: Confirm that the selected file is actually an image the browser can display and that the URL has not been revoked while it is still in use. A picker hint does not guarantee valid image content.
  • The preview vanishes after replacement: Check whether cleanup is revoking the newly created URL rather than the previous one. Keep each URL associated with the selection that created it, and make cleanup follow that URL’s lifecycle.
  • The preview vanishes after loading: Do not revoke the object URL in the image’s load handler if the user still needs to view or interact with the image.
  • Clearing does nothing: Ensure the clear action updates both the file state and the URL state. If handling picker cancellation, treat an empty file list as a deliberate cleared selection.
  • An upload fails despite a working preview: Previewing is local display only. Check the upload endpoint, request handling, server validation, and storage flow separately.
  • The wrong image appears after fast selections with FileReader: Prevent an older read result from updating state after a newer selection, for example with a request identity check.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a webpage for a test, report, or agent workflow—not to preview a user’s selected upload—ScreenshotNeo is a screenshot API and MCP server. Its one-request endpoint returns a screenshot or PDF; it is separate from the local React file-preview pattern above. For its request options, see the ScreenshotNeo documentation.

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

The returned image is a capture of the requested webpage, not a preview of a file selected in your React input. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. An MCP server lets AI agents take screenshots, and the free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for the free plan.

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

Frequently Asked Questions

Does showing the React preview upload my image?

No. It displays the browser-selected file locally; upload requires a separate request from your application.

Should I use an object URL or FileReader?

For a temporary image preview, an object URL is a straightforward fit. Use FileReader when your application specifically needs a data URL.

Can I trust accept=”image/*” to reject unsafe files?

No. It guides the picker, but validation must be designed separately and performed on the server as part of upload handling.

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.