Handle files
Published
In one line: declaring file_handlers in your Web App Manifest registers your
installed PWA with the OS as a handler for specific file types, so double-clicking or
“Open with”-ing a matching file launches your app with that file instead of you building
a separate import flow.
1. Declare file_handlers in the manifest
Section titled “1. Declare file_handlers in the manifest”Per Chrome for Developers, each file handler needs an action URL within the app’s
scope, an accept map of MIME types to file extensions, and an optional icons array
so the OS can show a file-type-specific icon instead of just the app icon:
{ "file_handlers": [ { "action": "/open-csv", "accept": { "text/csv": [".csv"] }, "icons": [ { "src": "/icons/csv-icon.png", "sizes": "256x256", "type": "image/png" } ], "launch_type": "single-client" } ]}launch_type controls whether opening multiple matching files at once reuses a single app
window ("single-client", the default) or, with "multiple-clients", launches the app once
per file, each launch’s LaunchParams.files array holding just that one file.
2. Consume the launched file with launchQueue
Section titled “2. Consume the launched file with launchQueue”Per MDN, once the OS launches the app with a matching file, the app receives it through
window.launchQueue.setConsumer():
window.launchQueue.setConsumer(async (launchParams) => { if (!launchParams.files.length) { return; } const [fileHandle] = launchParams.files; const file = await fileHandle.getFile(); const text = await file.text(); renderCsv(text);});Where it’s supported
Section titled “Where it’s supported”- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop) | Yes | 102 | high | source | — |
| Chrome (Android) | No | — | high | source | 1 |
| Edge (Desktop) | Yes | 102 | high | source | 2 |
| Firefox (Desktop) | No | — | high | source | 3 |
| Firefox (Android) | No | — | high | source | 45 |
| Safari (macOS) | No | — | high | source | 6 |
| Safari (iOS) | No | — | high | source | 78 |
| Samsung Internet | No | — | high | source | 910 |
| WebView (Android) | No | — | high | source | 1112 |
- No Chrome Android support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Chrome.
- No Firefox support is recorded in browser-compat-data.
- No Firefox for Android support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Firefox.
- No Safari support is recorded in browser-compat-data.
- No Safari on iOS support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Safari.
- No Samsung Internet support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Chrome Android.
- No WebView Android support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Chrome Android.
Per Chrome for Developers, File Handling is “limited to desktop operating systems” in Chromium’s implementation — do not rely on it to associate files with the app on Android.
3. Detect support and fall back
Section titled “3. Detect support and fall back”Per Chrome for Developers, feature-detect with both "launchQueue" in window and
"files" in LaunchParams.prototype, since launchQueue alone does not confirm the
files parameter shape the consumer depends on. When either check fails, give users an
in-app way to open a file that doesn’t depend on OS registration:
function supportsFileHandling() { return 'launchQueue' in window && 'files' in LaunchParams.prototype;}
if (supportsFileHandling()) { window.launchQueue.setConsumer(async (launchParams) => { if (!launchParams.files.length) return; const file = await launchParams.files[0].getFile(); renderCsv(await file.text()); });} else { // No File Handling support here — fall back to a manual // <input type="file" accept=".csv"> picker in the UI. document.getElementById('open-file-input').hidden = false;}What goes wrong
Section titled “What goes wrong”-
file_handlersregistration is desktop-only in Chromium, per Chrome for Developers — always ship an in-app file picker as the Android/fallback path. - Feature-detect with both
"launchQueue" in windowand"files" in LaunchParams.prototype, per Chrome for Developers — checkinglaunchQueuealone doesn’t confirm thefilesparameter shapesetConsumer()callbacks depend on. - Per Chrome for Developers,
launchQueuequeues launches until a consumer is registered, and each launch is handled exactly once regardless of when the consumer is set — but the consumer function itself must still run before you can read the launched file, so don’t gate its registration behind other async startup work. - Firefox and Safari do not implement
file_handlers— do not depend on OS-level file association as the only way into this feature.
Where to go next
Section titled “Where to go next”- manifest: file_handlers support — the full
compatibility data and
file_handlersmanifest reference. - Receive shared content (share target) — a related guide for receiving files and data shared from other apps via the OS share sheet.