Skip to content
TargetQA
Playwright · TypeScriptFile Operations & NetworkIntermediate4 min read

How to Upload Files to Hidden or Drag-and-Drop Inputs in Playwright

Modern file uploaders hide the real <input type='file'> behind styled dropzones. Learn how Playwright bypasses visibility requirements to set files instantly.

By TargetQA Engineering Team•Updated 2026-03-10
Interactive Cloud Sandbox

Test this solution live in real cloud browsers

Preload the working test script directly into TargetQA's Playwright compiler. Zero local dependencies or browser drivers needed.

Test Live in Compiler
The Problem & Error Symptoms

Understanding the Breakdown

Modern frontend frameworks style file uploaders by setting `<input type='file' style='display: none;'>` and providing a custom button or drag-and-drop box. When automated scripts attempt to `.click()` the hidden input, the runner fails with: `locator.click: Target element is not visible` or `Element is hidden from view`.

Under the Hood

Why Traditional Selectors Fail

Standard user actions like `.click()` and `.fill()` in Playwright perform actionable checks (visible, stable, enabled). Because hidden inputs have `display: none` or zero height/width, clicking them directly fails. However, file inputs are not meant to be clicked in automation—they are meant to receive file descriptors.

Field-Tested Patterns

Production-Grade Solutions

Choose the strategy that matches your test architecture. Pattern #1 is recommended for most production test suites.

1. setInputFiles Directly on Hidden Input (Recommended)

Recommended

Playwright's `setInputFiles` deliberately bypasses visibility checks, allowing you to target hidden file inputs directly.

1. setInputFiles Directly on Hidden Input (Recommended) (typescript)
// Locate the hidden <input type="file"> directly, even with display: none or opacity: 0:
const fileInput = page.locator('input[type="file"]');

// Upload a single file from disk:
await fileInput.setInputFiles('test-assets/invoice.pdf');

// Or upload multiple files:
await fileInput.setInputFiles([
  'test-assets/photo1.png',
  'test-assets/photo2.png'
]);
How it works: `setInputFiles` does not require the element to be visible because it dispatches native input events directly to the file receiver.

2. In-Memory Buffer Upload (No Local File Needed)

Generate dynamic file content in memory without maintaining physical test fixtures on disk.

2. In-Memory Buffer Upload (No Local File Needed) (typescript)
// Create a file buffer dynamically on the fly:
await page.locator('input[type="file"]').setInputFiles({
  name: 'audit-report.csv',
  mimeType: 'text/csv',
  buffer: Buffer.from('id,name,role\n1,Alex,QA\n2,Sam,SDET'),
});
How it works: In-memory uploads are clean, fast, and prevent file path dependency issues across different operating systems in CI/CD pipelines.

3. Handling Custom Dropzones via filechooser Event

If the input only mounts dynamically when a custom dropzone is clicked, intercept the browser file chooser.

3. Handling Custom Dropzones via filechooser Event (typescript)
const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('.custom-dropzone-button').click();
const fileChooser = await fileChooserPromise;

await fileChooser.setFiles('test-assets/avatar.png');
How it works: The `filechooser` event catches the native file selection dialog and simulates user file selection automatically.
Full Executable Cloud Test
Open in compiler

Complete Working Test Script

You can copy this exact test into your repository or run it immediately in our cloud sandbox against live demo targets.

test-recipe.spec.ts
import { test, expect } from '@playwright/test';

test('Upload file to hidden file input without local fixtures', async ({ page }) => {
  // Navigate to herokuapp upload page
  await page.goto('https://the-internet.herokuapp.com/upload');

  // Locate the input[type=file] element
  const fileInput = page.locator('#file-upload');

  // Upload an in-memory test file dynamically (zero external dependencies)
  await fileInput.setInputFiles({
    name: 'targetqa-test-sample.txt',
    mimeType: 'text/plain',
    buffer: Buffer.from('Automated test file generated by TargetQA cloud runner.'),
  });

  // Submit the form
  await page.locator('#file-submit').click();

  // Verify successful upload confirmation
  const uploadedHeader = page.locator('h3');
  await expect(uploadedHeader).toHaveText('File Uploaded!');

  const uploadedFiles = page.locator('#uploaded-files');
  await expect(uploadedFiles).toContainText('targetqa-test-sample.txt');

  console.log('Successfully uploaded in-memory file to hidden input!');
});
Target framework: Playwright · Language: TypeScript
Preload & Run in Compiler
Production Checklist

Best Practices & Golden Rules

  • Use in-memory buffers `{ name, mimeType, buffer }` to avoid fragile filesystem paths in CI/CD.
  • Target the `input[type='file']` selector directly rather than clicking the stylized button.
  • Reset file inputs when needed by passing an empty array: `await fileInput.setInputFiles([])`.
  • Avoid using OS-level GUI tools (like AutoIt or Robot) which are completely unnecessary in modern frameworks.
Avoid Test Flakiness

Common Pitfalls to Avoid

  • Calling `.click()` on the `<input type='file'>`: this triggers an OS file dialog which freezes non-Playwright runners.
  • Using hardcoded Windows absolute paths like `C:\Users\...` that break when executed on Linux CI runners.

Frequently Asked Questions

Can setInputFiles work if the input element has display: none?

Yes! Playwright specifically exempts `setInputFiles` from visibility checks because modern web development standard practices frequently hide native file inputs.

How do I upload multiple files at once?

Pass an array of file paths or buffer objects to `setInputFiles(['file1.png', 'file2.png'])` on an input that supports the `multiple` attribute.

How do I clear an uploaded file from an input?

Call `await fileInput.setInputFiles([])` with an empty array to reset the element's value.

Hands-On Practice

Practice This Skill in TargetQA Challenges

Explore More Automation Guides