Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteSome links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use a file input, read the first selected File, create a temporary URL with URL.createObjectURL(file), and render that URL in an <img>. This displays the image locally; it does not upload, validate, or store it. Keep the original File for your later upload and revoke each object URL when its preview is replaced or removed.
Complete React image-preview component
The component below supports one image, handles cancellation, clears an existing selection, releases object URLs, and keeps the original File available for submission. It uses a ref for the active URL so replacement cleanup happens immediately and unmount cleanup does not depend on a stale state value.
import { useEffect, useRef, useState } from 'react';
export default function ImagePicker() {
const [file, setFile] = useState(null);
const [previewUrl, setPreviewUrl] = useState(null);
const inputRef = useRef(null);
const objectUrlRef = useRef(null);
function releasePreview() {
if (objectUrlRef.current) {
URL.revokeObjectURL(objectUrlRef.current);
objectUrlRef.current = null;
}
}
function handleChange(event) {
const nextFile = event.target.files?.[0] ?? null;
releasePreview();
setFile(nextFile);
if (nextFile) {
const nextUrl = URL.createObjectURL(nextFile);
objectUrlRef.current = nextUrl;
setPreviewUrl(nextUrl);
} else {
setPreviewUrl(null);
}
}
function clearSelection() {
releasePreview();
setFile(null);
setPreviewUrl(null);
// Allows selecting the same file again after clearing it.
if (inputRef.current) inputRef.current.value = '';
}
useEffect(() => {
return () => releasePreview();
}, []);
async function handleSubmit(event) {
event.preventDefault();
if (!file) return;
const formData = new FormData();
formData.append('image', file);
const response = await fetch('/api/images', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`);
}
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="image-file">Choose an image</label>
<input
ref={inputRef}
id="image-file"
type="file"
accept="image/*"
onChange={handleChange}
/>
{previewUrl && (
<figure>
<img
src={previewUrl}
alt={file ? `Preview of ${file.name}` : 'Preview of selected image'}
style={{ maxWidth: '100%', height: 'auto' }}
/>
<figcaption>{file?.name}</figcaption>
</figure>
)}
<button type="button" onClick={clearSelection} disabled={!file}>
Remove image
</button>
<button type="submit" disabled={!file}>Upload image</button>
</form>
);
}
accept="image/*" guides the picker toward image files, but it is not a security boundary. A user can provide a file with misleading metadata, so enforce your size and content rules in the browser for useful feedback and repeat authoritative validation on the server.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchHow the preview flow works
- The input emits a change event. React passes the event to
handleChange; the selected files are inevent.target.files. - The handler chooses a file. The example takes the first entry and deliberately handles the no-file case, which occurs when the picker is cancelled or the control is cleared.
- The browser creates a local object URL.
URL.createObjectURL(file)returns a temporary URL that an image element can display without sending the file to your server. - React renders only when a URL exists. Conditional rendering avoids an empty
src, which can cause an image request you did not intend. - The original file remains the upload payload. Submit
filewithFormData; do not send the object URL as if it were the image itself.
Previewing is therefore a client-side display step. It does not prove that the image is safe, accepted by your API, uploaded successfully, or stored permanently.
#1 Best Overall
Releasing object URLs correctly
Every call to URL.createObjectURL creates a distinct URL. Release the previous one before replacing a preview, release it when the user removes the image, and release it when the component unmounts. The example’s releasePreview function centralizes those paths.
Do not revoke the URL immediately after the image’s load event if the user still needs to view, zoom, interact with, or otherwise use the preview. Keep it alive for the preview’s lifetime, then revoke it during replacement, removal, or unmount. Avoid creating object URLs during render: React can render repeatedly, and render-time side effects can leave URLs without a matching cleanup.
Validation before the upload
Check presence and size
const MAX_BYTES = 5 * 1024 * 1024;
function validateImage(file) {
if (!file) return 'Choose an image first.';
if (!file.type.startsWith('image/')) return 'Select an image file.';
if (file.size > MAX_BYTES) return 'The image must be 5 MB or smaller.';
return null;
}
Call this function before enabling submission or before constructing FormData. Treat the browser check as user feedback, not as the final trust decision; inspect the actual content and enforce limits again on the server.
Check dimensions when your product needs them
If a profile photo or banner has minimum dimensions, load the preview in an Image object and inspect its natural width and height before upload. Keep the object URL until that check and the user’s preview are finished, then release it through the same cleanup path. Do not claim that a file’s extension or MIME string alone establishes its dimensions or safety.
Use useful alternative text
Give the preview an alt that identifies what it represents, such as the selected filename or “Preview of selected image.” If the image is purely decorative and a nearby text label already conveys its purpose, use an empty alt instead. Keep the file input’s visible label associated with it through htmlFor and id.
Uploading the selected File
The preview and upload are separate actions. A typical submit handler sends the original file as multipart form data:
async function uploadImage(file) {
const error = validateImage(file);
if (error) throw new Error(error);
const body = new FormData();
body.append('image', file, file.name);
const response = await fetch('/api/images', {
method: 'POST',
body
});
if (!response.ok) {
throw new Error(`Upload failed with HTTP ${response.status}`);
}
return response.json();
}
When using FormData, let the browser set the multipart boundary; do not manually set a Content-Type header unless your server integration specifically requires a correctly generated boundary. Add progress, retry, authentication, and server response handling according to your API rather than treating a rendered preview as evidence that the request succeeded.
Object URL versus FileReader data URL
FileReader.readAsDataURL(file) is the other browser-native approach. It asynchronously reads the File or Blob and exposes a data: URL when reading completes.
import { useEffect, useState } from 'react';
function DataUrlPreview({ file }) {
const [dataUrl, setDataUrl] = useState(null);
useEffect(() => {
if (!file) {
setDataUrl(null);
return;
}
const reader = new FileReader();
reader.onload = () => setDataUrl(typeof reader.result === 'string' ? reader.result : null);
reader.onerror = () => setDataUrl(null);
reader.readAsDataURL(file);
return () => {
reader.onload = null;
reader.onerror = null;
if (reader.readyState === FileReader.LOADING) reader.abort();
};
}, [file]);
return dataUrl ? <img src={dataUrl} alt="Preview of selected image" /> : null;
}
| Aspect | Object URL | Data URL with FileReader |
|---|---|---|
| Preview source | A temporary browser URL that references the File or Blob | Encoded file contents in a data: URL |
| Lifecycle | Explicitly revoke the URL when its preview use ends | Wait for the asynchronous read; retain the resulting string only as needed |
| Best fit | A temporary local display | When another part of the application specifically needs a data URL |
Choose based on the representation and lifecycle your application needs. There is no measured performance conclusion here that justifies treating one as universally faster.
Rank #3
Multiple-image previews
For a gallery, add multiple, map every selected file to a preview record, and keep each URL paired with its file:
const nextItems = Array.from(event.target.files ?? []).map((file) => ({
file,
url: URL.createObjectURL(file)
}));
setItems(nextItems);
Before replacing the array, iterate over the old records and call URL.revokeObjectURL(item.url). When one item is deleted, revoke only that item’s URL. On unmount, revoke every remaining URL. Do not keep a single shared URL ref for multiple files.
Common problems and fixes
The preview is blank
- Render the
imgonly when the URL is non-null; never pass an empty string assrc. - Confirm that the handler reads
event.target.files?.[0], not a nonexistentevent.target.file. - Inspect the browser console for an image decoding error and verify the selected file is actually an image.
Choosing Cancel removes the old preview unexpectedly
Decide your product behavior explicitly. The example treats a no-file change as a deliberate clear. If cancellation should preserve the existing image, return early when files?.[0] is absent and provide a separate Remove button for clearing.
The same file cannot be selected twice
After removing a file, set the input’s value to an empty string, as clearSelection does. This lets a later selection of the same path produce a new change event.
Rank #4
Memory usage grows after repeated replacements
Revoke the prior URL before creating its replacement and revoke all URLs on unmount. Do not create URLs in render or in an effect that runs on every render without a matching cleanup.
The upload succeeds but the server rejects the image
That is expected when server rules differ from client hints. Return a clear API error, show it near the input, and validate content, dimensions, and size on the server. A local preview does not bypass those checks.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →The image disappears after immediate revocation
Do not revoke in the image’s onLoad handler while the user still needs the displayed image. Revoke when the preview is replaced, removed, or the component unmounts.
Performance and reliability choices
- Keep only the state you need: store the original
Fileand its current preview URL, not duplicate encoded data unless your application needs it. - Prevent oversized work: reject clearly excessive files before reading or uploading them, while still enforcing the rule on the server.
- Handle races: if a user selects files quickly, cancel an in-progress
FileReaderand ignore stale asynchronous callbacks. - Preserve user control: do not revoke a still-visible preview merely because it finished loading.
- Separate states: represent “no selection,” “preview ready,” “uploading,” “uploaded,” and “upload failed” independently so a failed request does not erase a usable local preview.
Or skip the browser setup
If what you actually need is an automated screenshot of a rendered page or preview workflow—not a local file preview inside your React form—ScreenshotNeo provides a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Before capture it can accept cookie banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP tools—take_screenshot, get_page_info, and capture_pdf—work with Claude, Cursor, and other MCP clients.
cURL (see the ScreenshotNeo API documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/image-preview -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/image-preview"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/image-preview' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Every feature is available on every plan: full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF controls, HTML/CSS rendering, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and familiar parameter names for easier switching.
Best Value
| Plan | Allowance and price |
|---|---|
| Free | 1,000 shots per month, no card |
| Starter | $5 for 3,000 shots |
| Growth | $15 for 15,000 shots |
| Pro | $39 for 60,000 shots |
| Scale | $99 for 250,000 shots |
| Business | $249 for 1,000,000 shots |
Yearly billing gives two months free. Start with 1,000 free ScreenshotNeo screenshots a month with no card, or use a paid plan starting at $5 for 3,000 shots.
Final checklist
- Use
accept="image/*"as a picker hint, not as validation. - Read the selected
Filefrom the input’sfilescollection. - Create the preview URL in an event handler or controlled effect, never during render.
- Render the image only when the source is nonempty.
- Keep the original
FileforFormDataupload. - Revoke each object URL after replacement, removal, or unmount, but not while the user still needs the preview.
- Validate again on the server.
Frequently Asked Questions
Can I submit the object URL to my upload endpoint?
No. The object URL is only a browser reference for rendering. Submit the original File, normally in FormData.
Do I need FileReader for a normal React preview?
No. URL.createObjectURL is sufficient for a temporary display. Use FileReader when another part of the application specifically requires a data URL.
What should happen when a user cancels the file picker?
Choose deliberately between preserving the current selection and clearing it. Handle the no-file case explicitly rather than assuming a File is always present.
Quick Recap
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.
Recommended Free Tools

