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.
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.
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`.
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.
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)
RecommendedPlaywright's `setInputFiles` deliberately bypasses visibility checks, allowing you to target hidden file inputs directly.
// 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'
]);2. In-Memory Buffer Upload (No Local File Needed)
Generate dynamic file content in memory without maintaining physical test fixtures on disk.
// 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'),
});3. Handling Custom Dropzones via filechooser Event
If the input only mounts dynamically when a custom dropzone is clicked, intercept the browser file chooser.
const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('.custom-dropzone-button').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles('test-assets/avatar.png');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.
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!');
});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.
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.