Free tools Windows power users keep installed
One-click scans. No signup required.
To upload an image with Selenium, target the page’s <input type="file"> element and pass it an existing image path with send_keys(). Selenium does not control the operating-system file-picker window. Uploading the file and inserting it at the cursor in a rich-text editor are separate operations: after the upload, use the editor’s own insert, attach, or confirm control and verify that the image appears in the content.
What Selenium actually automates
Selenium’s documented file-upload method works with an HTML file input. The browser session receives the path through WebDriver, so no native dialog needs to open. Selenium’s official guide explains that, because WebDriver cannot interact with the file-upload dialog, it provides a way to upload files without opening it: the file input receives the path directly.
This distinction matters for an “inline image” workflow. A successful send_keys() call only supplies a file to the page. The application may then upload it immediately, show a thumbnail, require an Insert button, or expose a separate editor command that places the image at the current selection. Selenium cannot infer that application-specific behavior.
- Standard upload: locate
input[type="file"]and callsend_keys()with the image path. - Editor insertion: click the site’s upload/insert/confirm control, or complete its toolbar workflow.
- Verification: inspect the editor or saved page and confirm that an image element or rendered image is present where expected.
Prerequisites and a reliable local setup
- Python and Selenium 4 installed in the environment running the test.
- A WebDriver-supported browser and a driver configuration appropriate to that browser.
- An image file that already exists and is readable by the Python process.
- The page’s actual file-input selector and the editor controls used after upload.
Use a path that is unambiguous from the script’s working directory. Resolving it to an absolute path is especially useful when a test runner starts the script from a different directory. Selenium’s Python API documentation discusses path handling and send_keys() for file inputs: selenium.webdriver.remote.webelement.
Recommended Free Tools
#1 Best Overall
Complete Python example
The following example uploads a local image, then leaves placeholders for the target application’s editor-specific action. Replace the URL, image location, and locators with those from your page.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
IMAGE = Path("assets/example.png").resolve()
if not IMAGE.is_file():
raise FileNotFoundError(f"Image not found: {IMAGE}")
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 20)
try:
driver.get("https://example.com/editor")
# This is the standard WebDriver upload step.
file_input = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='file']"))
)
file_input.send_keys(str(IMAGE))
# Adapt this to the application. Examples include an Insert button,
# an Upload confirmation, or a toolbar command.
insert_button = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-action='insert-image']"))
)
insert_button.click()
# Verify the editor-specific result. Use the real selector for your page.
wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".editor img"))
)
finally:
driver.quit()
The selector button[data-action='insert-image'], the .editor img check, and the example URL are deliberately illustrative. The only universal part is locating the file input and sending the path. The Selenium documentation’s demonstration button, file-submit, belongs to that demonstration page and is not a general locator.
Finding the correct file input
Inspect the rendered HTML
Use browser developer tools to identify the real input. A typical control looks like <input type="file" name="image" accept="image/*">. Prefer a stable attribute supplied by the application:
file_input = driver.find_element(By.NAME, "image")
# or
file_input = driver.find_element(By.ID, "image-upload")
# or
file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file'][accept^='image']")
If several file inputs exist, scope the selector to the dialog or form that belongs to the editor. Do not assume the first matching input is the image uploader; pages often contain separate inputs for avatars, attachments, and documents.
Hidden inputs and custom drop zones
Many drag-and-drop interfaces hide the native input behind a styled drop zone. If the element exists in the DOM, sending the path to that input is still the normal WebDriver route. If the site creates the input only after you click “Add image,” perform that click first, then wait for the input to appear. If there is no file input at all, the interface may use an application-specific picker or an API; Selenium’s generic upload method cannot replace that implementation.
Rank #2
Making the image appear inline in a rich-text editor
After send_keys(), watch what the application does. Common workflows include:
- Immediate insertion: the editor uploads the file and inserts an image at the current caret position. Wait for the editor’s image node or preview.
- Modal confirmation: the upload finishes in a dialog. Click the dialog’s Insert, Done, or Apply button.
- Upload followed by selection: the image appears in a media library. Select it, then click Insert.
- Separate toolbar command: uploading stores the asset, while an Image toolbar button inserts the selected asset into the document.
Use explicit waits for state changes rather than fixed sleeps. For example, wait for a progress indicator to disappear, an image preview to become visible, or an Insert button to become enabled. Then verify the editor’s actual representation. Depending on the editor, that may be an <img> inside a contenteditable region, an iframe document, or a serialized hidden field.
Editors inside an iframe
If the editor is in an iframe, switch into it before locating the content or toolbar controls, and switch back when interacting with the surrounding page:
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteframe = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "iframe.editor-frame")))
driver.switch_to.frame(frame)
# Locate editor-specific controls or verify the inserted image here.
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "img")))
driver.switch_to.default_content()
The file input may be outside the iframe, inside it, or created by a modal in the top-level document. Inspect the page and switch contexts accordingly.
Local versus remote WebDriver
A local run normally has one filesystem: the Python process and browser can resolve the same absolute path. In a remote setup, the browser may run on another machine or in a container. A path such as /home/me/photo.png on your laptop may not exist where the browser session runs. Selenium’s API guidance highlights this path and remote-execution concern.
- Place the image where the remote test environment can read it, using a shared volume or test fixture.
- Use the remote WebDriver’s file-upload support where your Selenium configuration provides it, so the file is transferred to the browser node.
- Log the resolved path and check that the file exists before calling
send_keys(). - Keep credentials and temporary uploads out of shared locations, and remove temporary files after the test.
Do not “solve” a remote path problem by opening the operating-system picker. WebDriver is designed to avoid that dialog; make the file available to the session instead.
Useful waits and verification patterns
Wait for an uploaded preview
file_input.send_keys(str(IMAGE))
preview = wait.until(
EC.visibility_of_element_located((By.CSS_SELECTOR, ".upload-preview img"))
)
assert preview.get_attribute("src")
Wait for an enabled insertion control
insert = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button.insert-image"))
)
insert.click()
Verify the saved content
For an editor that serializes HTML, read the editor’s value or content area after insertion and assert that it contains the expected image reference. For a preview page, wait for the rendered image and check its src, dimensions, or an application-provided asset identifier. Verification should test the outcome the user needs, not merely that the file input’s value changed.
Outdated 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 matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallTroubleshooting
“Unable to locate element”
The selector may be wrong, the input may be created later, or you may be in the wrong frame. Inspect the live DOM, wait for the element after opening the upload UI, and switch into the correct iframe before searching.
“Element is not interactable”
A custom widget may still expose a hidden file input, but the page may not have initialized it yet. Trigger the widget’s Add image action first and wait for the input. Avoid clicking the native file input; send the path directly once the element is present.
File-not-found or upload rejected
Resolve the path with Path.resolve(), check is_file(), and confirm that the process has read permission. Check the application’s accepted MIME types, maximum size, and image-format rules. In remote execution, verify the file on the browser node or use the session’s transfer mechanism.
The upload succeeds but no inline image appears
This is an editor workflow issue, not proof that Selenium failed. Look for a required Insert/Apply step, select the uploaded asset in a media dialog, restore focus to the editor, and verify the resulting content. If the editor is iframe-based, inspect the iframe document rather than the outer page.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →The test is flaky
Replace arbitrary delays with explicit waits for upload completion, preview visibility, network-driven status changes exposed by the UI, and editor insertion. Use stable IDs, names, or data attributes instead of deeply nested CSS paths. Capture a screenshot and page source on failure so you can distinguish a selector change from a server-side rejection.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is a clean image of a webpage rather than placing a local file into an editor, ScreenshotNeo returns a screenshot or PDF from one request. It accepts cookie/consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
See the complete parameter reference in the ScreenshotNeo documentation. A direct call looks like this:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python and Node.js equivalents:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo also supports full-page and element capture, dark mode, device presets, retina scale, PDF controls, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account to try it without a card.
Best Value
When to use Selenium for this job
- Use Selenium when you must exercise the same upload and editor controls a user operates.
- Use a direct application API when the site documents one and your test is about backend asset creation rather than browser behavior.
- Use ScreenshotNeo when the deliverable is a webpage screenshot or PDF and you do not need to edit page content.
The key test boundary is simple: Selenium can deliver the file to an HTML file input, but only the target application can define how that uploaded asset becomes inline content.
Frequently Asked Questions
Can Selenium upload an image by typing into the operating-system file dialog?
No. Locate the page’s file input and pass the path with send_keys(); this avoids the native dialog entirely.
Does send_keys() insert the image at the cursor automatically?
Not necessarily. It uploads or supplies the file to the page. The editor may require a separate Insert, Apply, or media-selection action.
Why does a path work locally but fail on a Selenium Grid node?
The browser is running on a different filesystem. Put the fixture where the remote node can read it or use the remote WebDriver upload-transfer support configured for your environment.
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.

