How to Upload Files with Playwright

Learn how to handle file uploads in Playwright with step-by-step examples and best practices. Run these tests on BrowserStack across thousands of real browsers and devices.

Get Started free
How to Upload Files with Playwright
Home Guide How to Upload Files with Playwright

How to Upload Files with Playwright

Ever tried automating file uploads in Playwright and hit a wall because the file chooser didn’t behave as expected?

I did that recently while testing a complex form, and it was surprisingly tricky.

Sometimes the input was hidden, other times the app triggered a custom file dialog that refused to accept the file programmatically.

Figuring out how to handle single and multiple file uploads reliably took some trial and error, and that’s exactly what I want to share.

Overview

Playwright handles file uploads through setInputFiles(), which attaches one or more files to an input type=”file” element. Once the input is located, you can assign files directly without interacting with the browser’s native dialog.

1. Uploading a Single File

from playwright.sync_api import sync_playwright

with sync_playwright() as pw:
browser = pw.chromium.launch()
page = browser.new_page()
page.goto(“https://sample-app.com/upload”)

# Create a sample file
with open(“demo_file.txt”, “w”) as f:
f.write(“Example content”)

# Attach the file
page.locator(‘input[type=”file”]’).set_input_files(“demo_file.txt”)

browser.close()


2. Uploading Multiple Files
# Create sample files
with open(“a.txt”, “w”) as f: f.write(“A”)
with open(“b.txt”, “w”) as f: f.write(“B”)

# Attach multiple files
page.locator(‘input[type=”file”]’).set_input_files([“a.txt”, “b.txt”])


3. Using a File Chooser
Some apps trigger a file dialog through a custom button. Use Playwright’s filechooser event to supply files.
with page.expect_file_chooser() as fc:
page.get_by_text(“Select File”).click()

chooser = fc.value
chooser.set_files(“demo_file.txt”)

In this guide, I’ll walk through practical approaches to uploading files with Playwright, covering hidden inputs, multiple file selections, and working with real browser behaviors.

How Playwright Handles File Uploads

Playwright attaches files directly to the browser’s input type=”file” element instead of interacting with the operating system’s native dialog. This keeps uploads stable because everything happens inside the browser’s context.

However, it also means complex UI wrappers, hidden inputs, or custom buttons do not change how Playwright assigns files.

This creates a consistent workflow across different upload patterns, and the core behaviors remain the same regardless of how the application exposes the file selection UI.

Key capabilities include:

  • Direct attachment: Files can be assigned to any file input using setInputFiles(), and the browser treats them as user-selected.
  • Multiple file support: A single call can attach several files, and the browser fires change events accordingly.
  • In-memory uploads: FilePayload lets tests create virtual files without writing anything to storage.
  • Dialog handling: The filechooser event helps when the app opens a custom picker instead of revealing the file input.
  • State simulation: The browser updates the input’s file list and triggers all related events exactly as it would for a real user.

Even with Playwright’s stable file upload handling, issues can still appear in different browsers or devices. Platforms like BrowserStack let you test uploads on real devices and browsers, and provide detailed analytics to quickly spot failures, track file-handling issues, and ensure your upload flows work reliably for all users.

Run Playwright Tests Now

Step-by-Step Guide: Uploading a Single File in Playwright

A single-file upload sounds simple, but the real input is often hidden, wrapped inside a custom component, or paired with scripts that rely on precise change events. Playwright avoids the native dialog and updates the file input directly, so the upload works reliably even when the UI around the element is complex.

This keeps the workflow consistent and allows the test to control the actual input rather than the visible button.

Follow these steps to upload a single file:

  • Step 1: Locate the actual element. Frameworks often mask it behind custom buttons, so target the real input inside the DOM, including within containers or shadow roots if needed.
  • Step 2: Prepare the file you want to upload. This can be a pre-existing file, a temporary file created during the test, or a virtual file you plan to replace with FilePayload.
  • Step 3: Attach the file using setInputFiles(). Playwright rewrites the FileList, updates the input’s value, and triggers the change event exactly as a user action would.
  • Step 4: Complete any additional workflow. Some applications validate the file, display previews, or require a submit action, so wait for these UI or network changes to finish before moving on.

Step-by-Step Guide: Uploading Multiple Files in Playwright

Uploading more than one file introduces additional behavior in the UI because many frameworks validate counts, enforce type restrictions, or render previews for each file.

Playwright stays consistent by assigning an array of file paths to the input, and the browser updates the FileList exactly as if multiple selections were made in a native dialog.

This makes multi-file workflows manageable even when the application builds dynamic elements based on the number of files. This ensures predictable behavior across forms that accept multiple documents, images, or mixed file types.

Follow these steps to upload multiple files:

  • Step 1: Identify an input type=”file” element configured for multi-selection. Look for the multiple attribute or confirm that the application expects more than one file before proceeding.
  • Step 2: Prepare the files you want to include. These may be existing assets in your test project or temporary files created during execution. Each file should be individually controlled so the test can validate the order, count, or type.
  • Step 3: Attach all files at once using setInputFiles([…]). Playwright updates the input’s FileList with every file in the array, and the browser fires a single change event that contains all selected items.
  • Step 4: Handle UI updates that follow multi-file selection. Applications often generate thumbnails, file rows, size checks, or validation prompts, so allow these actions to complete before moving forward with assertions or subsequent steps.

Uploading Virtual Files with FilePayload in Playwright

Some tests require files that do not exist on disk, such as dynamically generated documents, JSON payloads, or lightweight samples created only for the duration of the test.

Playwright supports this through FilePayload, which builds a file entirely in memory and assigns it to the file input without writing anything to storage.

This helps when tests run in restricted environments or when the content must be constructed on the fly.

Follow these steps to upload a virtual file:

  • Step 1: Define the virtual file’s structure. A FilePayload requires a name, MIME type, and a byte buffer that represents the file’s contents.
  • Step 2: Pass the payload to setInputFiles(). Instead of a file path, provide an array containing the virtual file object so the browser treats it as a real, user-selected file.
  • Step 3: Allow the application to process the new file. UI components may parse the content, display metadata, or run validations based on MIME type and size, so wait for these updates before continuing.

How to Clear Selected Files in Playwright

Clearing an uploaded file is a common part of real workflows, especially when users replace incorrect files or adjust submissions. Most interfaces offer a remove or reset action, but the underlying behavior varies.

Some components simply wipe the input value, others recreate the input node entirely, and many wrap the logic inside custom buttons or icons. The challenge in testing this is identifying what the UI actually does when a file is removed.

Playwright needs to track whether the input is reset, whether previews disappear, and whether the application correctly updates its internal state.

Here’s how to clear selected files in Playwright:

  • Clear by resetting the input:Use setInputFiles([]) on the underlying to empty its FileList when the element is still accessible in the DOM.
  • Clear through UI controls: If the interface exposes a remove or delete button, trigger that action so the application handles its own cleanup logic.
  • Clear when inputs are re-rendered: Some frameworks rebuild the file input after removal, so interact with the new input element that appears in the DOM.
  • Clear with JS-driven resets: If the component resets via script, wait for the input value to update and ensure related UI changes such as previews or counters disappear.

Even if file uploads work on a local setup, hidden inputs, dialog triggers, and preview scripts can behave differently on real browsers. BrowserStack allows you to verify every upload scenario on real devices with consistent accuracy.

Inline Banner Guide

Uploading Files Using File Chooser Dialogs

Some applications never expose the real element. Instead, they trigger a native-style file picker through a custom button or script. Playwright cannot interact with the operating system dialog, but it listens for the moment the browser requests one and injects the file before the dialog even appears.

The challenge comes from timing. The file chooser must be captured at the exact moment the page expects a file, and different UI frameworks trigger the dialog through different events.

How to work with file chooser dialogs:

  • Wait for the file chooser request: Use page.expect_file_chooser() to intercept the dialog when the user-facing button is clicked.
  • Provide the required file: Call file_chooser.set_files() with one or many files so the browser believes the dialog was completed.
  • Handle complex UI triggers: Some components open the dialog on nested elements, delayed animations, or JS listeners, so align the click action with the expected chooser event.
  • Verify downstream behavior: Dialog-triggered uploads often launch previews, metadata extraction, or async validation, so confirm these updates before proceeding.

Uploading Files with Hidden File Inputs

Many modern interfaces hide the actual file input and replace it with a styled button, drag-and-drop zone, or icon-based control.

Even though the input is invisible or set to display: none, Playwright can still interact with it because it communicates directly with the DOM. This removes the usual friction testers face when applications mask the real upload element behind layers of UI.

How to work with hidden file inputs:

  • Target the real input element: Use CSS selectors, role locators, or DOM exploration to find the actual input[type=”file”] that receives the file.
  • Ignore visibility restrictions: Playwright allows calling setInputFiles() on hidden inputs, so there is no need to unhide them or simulate a click.
  • Watch for input regeneration: Some components replace the input after an upload or removal, so locate the fresh element before attaching the next file.
  • Validate UI reactions: Hidden inputs often trigger previews, counters, or validation messages once populated, so verify these visual and network changes after the file is assigned.

Testing File Uploads in CI or Remote Browsers

File uploads behave differently in real infrastructure because CI systems, containerized environments, and remote browsers often lack local storage access or expose restricted file paths. Upload components may also respond differently on real devices or browsers with distinct security models.

BrowserStack helps here by providing clean, consistent environments where Playwright can attach files just as it would locally. The platform ensures stable paths, predictable browser behavior, and full support for both physical devices and desktop browsers.

Here’s how BrowserStack helps with file upload testing:

  • Consistent file handling across environments: BrowserStack standardizes browser behavior so file inputs, hidden elements, and change events behave the same across all test runs.
  • Reliable access to test files: Files can be generated or uploaded within BrowserStack sessions without worrying about OS-level restrictions or missing paths.
  • Cross-browser validation on real devices: Upload flows are tested on real Chrome, Safari, Firefox, and mobile browsers, catching issues that appear only in device-specific upload handlers.
  • Stable remote execution: BrowserStack isolates tests from local machine variations, ensuring that upload components render, validate, and process files in a predictable way every time.

Talk to an Expert

Common Errors When Uploading Files in Playwright and How to Fix Them

File uploads can fail for reasons that are not always obvious, especially when frameworks generate dynamic inputs, enforce strict validation, or trigger asynchronous scripts during selection.

Here are the most frequent issues and how to resolve them:

  • Incorrect or changing input selectors: Some UI libraries recreate the file input after each upload, which invalidates the previous locator. Re-query the input before calling setInputFiles() to ensure you attach files to the current DOM element.
  • Hidden or detached inputs: Inputs wrapped inside animations, shadow roots, or conditional render blocks may not be ready when the upload action runs. Wait for the input to be attached and stable before assigning files.
  • Unsupported file types or size checks: Applications may silently reject files due to MIME filters or client-side validation. Use files that match the expected format and confirm that the component has no constraints blocking the upload.
  • Race conditions during preview rendering: Some upload components trigger previews, thumbnails, or metadata extraction immediately after file assignment. Wait for these UI changes or network calls to complete before running assertions.

Conclusion

File uploads in Playwright span many real-world patterns, including hidden inputs, custom dialogs, and components that rebuild themselves during interaction. Each interface responds differently once a file is attached or cleared, and these differences shape how the test should approach the workflow.

Running these flows on BrowserStack brings consistency across browsers and devices. The platform removes environment-specific variations, stabilizes file handling, and ensures that upload behavior remains reliable in local, CI, and production-like conditions.

Try BrowserStack for Free

Tags
Automation framework Cross-browser automation Web app testing

Get answers on our Discord Community

Join our Discord community to connect with others! Get your questions answered and stay informed.

Join Discord Community
Discord