v146 · Web APIs · File Handling Flow
File Handling Flow
Walk through the complete file handling integration from manifest registration to reading the files inside the PWA — and see exactly where LaunchParams.targetURL fits and why Chrome 146 fixing it matters.
File Handling requires the page to be served as an installed PWA (installed from Chrome or Edge). The
launchQueue API is part of the Web App Launch Handler spec. This page explains the flow; actual file handling only works when installed.
integration flow
1
Declare file types in the manifest
Add a
file_handlers array to your manifest.json. Each entry declares an action URL and the MIME types and extensions your app handles.{
"name": "My Text Editor",
"file_handlers": [
{
"action": "/open",
"accept": {
"text/plain": [".txt", ".md"],
"text/csv": [".csv"]
}
}
]
}
2
User installs the PWA
Chrome registers the file type associations with the OS. On Windows: right-click any
.txt file → "Open with" now lists the PWA.3
User opens a registered file
Double-clicking a
.txt file (or using "Open with") triggers the PWA. Chrome launches the app to the action URL (/open).4
Register a launchQueue consumer
In the app's JavaScript, call
launchQueue.setConsumer() as early as possible. Chrome delivers the LaunchParams to this callback once the page is ready.if ('launchQueue' in window) {
launchQueue.setConsumer(async launchParams => {
// LaunchParams arrives here
openFiles(launchParams);
});
}
5
Read targetURL and files (Chrome 146)
The
LaunchParams object has both targetURL (the action URL the app was launched at) and files (array of FileSystemFileHandle). In Chrome 146, targetURL is no longer null.async function openFiles(launchParams) {
// Chrome 146: now populated
console.log('targetURL:', launchParams.targetURL);
// → "https://my-pwa.example/open"
for (const handle of launchParams.files) {
const file = await handle.getFile();
const text = await file.text();
editor.open(file.name, text);
}
}
before and after Chrome 146
Before Chrome 146
launchParams.targetURL
→ null ← bug: should be the action URL
launchParams.files
→ [FileSystemFileHandle, …] ✓
// Workaround needed:
const url = location.href;
// (had to infer from current URL)
Chrome 146+
launchParams.targetURL
→ "https://app.example/open" ✓ fixed!
launchParams.files
→ [FileSystemFileHandle, …] ✓
// No workaround needed — use targetURL directly
const url = launchParams.targetURL;
why targetURL matters
// A PWA may support multiple file types via different action URLs:
// "file_handlers": [
// { "action": "/open-image", "accept": { "image/*": [".png", ".jpg"] } },
// { "action": "/open-text", "accept": { "text/plain": [".txt"] } }
// ]
launchQueue.setConsumer(async launchParams => {
// Before Chrome 146: couldn't tell which action triggered the launch
// After Chrome 146: use targetURL to route correctly
const url = new URL(launchParams.targetURL);
if (url.pathname === '/open-image') {
await openImage(launchParams.files[0]);
} else if (url.pathname === '/open-text') {
await openText(launchParams.files[0]);
}
});
see also
scenario focus
Select a scenario to focus its rendered example and summary.