# file_handlers manifest member

> file_handlers registers an installed PWA as the OS opener for named MIME types and extensions; window.launchQueue then hands the opened file to the page.

`file_handlers` is an array of handler objects that, once the app is installed, registers the
PWA with the operating system as an opener for the MIME types and extensions it names. Opening a
matching file from Finder, Explorer, or the Files app launches the app at the handler's `action`
URL, and the page reads the file through `window.launchQueue`. The manifest member only creates
the registration; the page still has to consume the file in JavaScript.

Chrome 102 and Edge 102 process the member on Windows, macOS, Linux, and ChromeOS (BCD
`html.manifest.file_handlers`). Chrome for Android, Safari 27, Firefox 157, and Samsung Internet
drop it, so a file association from a PWA exists on desktop Chromium only.

## Member

- **Type**: array of objects. Each object has `action` (string URL), `accept` (object mapping a
  MIME type to an array of extensions), optional `icons` (array of image resources shown by the
  OS for files of that type), and optional `launch_type` (`"single-client"` or
  `"multiple-clients"`).
- **Default**: no registration. When the member is absent, or when the OS-level association
  is removed by the user, opening a file does not launch the app.
- **Example value**: `[{ "action": "/open", "accept": { "text/markdown": [".md"] } }]`.

Chromium validates each handler on its own. `action` must be a URL inside the manifest's
`scope`; an out-of-scope or unparsable `action` drops that handler with the parser message
`FileHandler ignored. Property 'action' is invalid.`, and an `accept` that is not an object of
MIME type to extension array drops it with `FileHandler ignored. Property 'accept' is invalid.`
(Chromium `manifest_parser.cc`). Extensions must start with a dot; the MIME type key is what
the OS registers, so `text/markdown` with `[".md", ".markdown"]` claims both suffixes.

`launch_type` defaults to `"single-client"`: opening several files at once delivers them to one
window as one `LaunchParams` with several entries in `files`. With `"multiple-clients"` each file
gets its own launch and `files` holds one handle. Windows ignores the distinction and launches
the app once per file regardless (developer.chrome.com, "Handle files in Progressive Web
Apps").

The first time a file is opened through a handler, Chromium shows a permission dialog before the
page sees the file. The grant is per app and per file type; Chromium resets it when a manifest
update changes `file_handlers`, and the user can revoke it from the app's site settings. On
Windows and macOS the association competes with native apps for the same extension, so the user
may still have to pick the PWA from the "Open with" menu.

:::observed
Chrome 155 on macOS 26 (English UI): after installing a PWA whose manifest declares
`"accept": { "text/markdown": [".md"] }`, the Finder context menu **Open With** lists the app by
its manifest `name`. Removing the `file_handlers` member and waiting for the daily manifest
update removes the entry again without a reinstall. In DevTools > Application > Manifest a
handler whose `action` is outside `scope` surfaces under **Errors and warnings** as
`FileHandler ignored. Property 'action' is invalid.`
:::

## Examples

The two manifests below register a Markdown editor; the script shows how the page receives the
handle and what it does when no file arrives.

### Registering a Markdown editor for two extensions

One handler claims both `.md` and `.markdown`, routes every launch to `/editor`, and supplies an
icon the OS can show on files of that type. `scope` is set explicitly so the `action` URL is
unambiguously inside it.

```json
{
  "name": "Draft",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "file_handlers": [
    {
      "action": "/editor",
      "accept": {
        "text/markdown": [".md", ".markdown"],
        "text/plain": [".txt"]
      },
      "icons": [{ "src": "/icons/markdown-file.png", "sizes": "256x256", "type": "image/png" }],
      "launch_type": "single-client"
    }
  ]
}
```

After installation on Chrome 102+ the OS lists Draft as an opener for `.md`, `.markdown`, and
`.txt`. Nothing changes on a browser that ignores the member: the manifest is still valid and
the app still installs.

### Consuming the launch and falling back to a file picker

The page at `/editor` installs a `launchQueue` consumer as early as possible, because launches
that arrive before the consumer is set are queued but a consumer set after a user action is too
late for the first file. When `launchQueue` is absent (any browser other than desktop Chromium),
the same function is wired to a button that opens `<input type="file">`.

```js
async function openHandle(handle) {
  const file = await handle.getFile();
  editor.value = await file.text();
  document.title = `${file.name} - Draft`;
}

if ('launchQueue' in window) {
  window.launchQueue.setConsumer(async (launchParams) => {
    if (!launchParams.files.length) return; // Launched from the icon, not from a file.
    for (const handle of launchParams.files) await openHandle(handle);
  });
} else {
  // No file-handling launch is possible here; offer the picker instead.
  openButton.hidden = false;
  openButton.addEventListener('click', () => picker.click());
  picker.addEventListener('change', async () => {
    const [file] = picker.files;
    if (file) editor.value = await file.text();
  });
}
```

`launchParams.files` holds `FileSystemFileHandle` objects, so the same handle can later be
passed to `createWritable()` to save back to the opened file without a save dialog; the
`<input type="file">` fallback yields plain `File` objects and cannot write back.

### Checking the registration from the OS side

There is no JavaScript API that reports whether the OS association exists. Verify it from the
operating system after install: on Windows 11 (English UI) open **Settings > Apps > Default
apps**, search for the extension, and the PWA appears under its manifest `name`; on macOS
select a matching file in Finder and use **File > Get Info > Open with**. If the app is absent,
check DevTools > Application > Manifest for an `Errors and warnings` line about the handler.

```json
{
  "file_handlers": [
    {
      "action": "https://other.example/open",
      "accept": { "text/markdown": [".md"] }
    }
  ]
}
```

This handler is dropped because `action` points at a different origin than the manifest's
scope; the console shows `Manifest: FileHandler ignored. Property 'action' is invalid.` and the
OS is not told about the type.

## See also

- [Handle files](/guides/file-handling/)
- [File System Access API: read and write local files](/reference/capabilities/file-system-access/)
- [launch_handler manifest member](/reference/manifest/launch-handler/)
- [scope manifest member](/reference/manifest/scope/)
- [Manifest Incubations: file_handlers member](https://wicg.github.io/manifest-incubations/#file_handlers-member) (wicg.github.io)
- [Let installed web applications be file handlers](https://developer.chrome.com/docs/capabilities/web-apis/file-handling) (developer.chrome.com)
- [Handle files in Progressive Web Apps](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files) (learn.microsoft.com)