# Receive shared content (share target)

> A step-by-step guide to registering an installed PWA as a share target — declaring share_target in the manifest, handling the GET or POST request it triggers, and storing shared files for the app to read.

import CompatTable from '@components/CompatTable.astro';

**In one line:** declaring `share_target` in your Web App Manifest registers your
installed PWA in the OS native share sheet, so other apps can share URLs, text, titles,
and files directly into your app instead of you building a separate import flow.

## 1. Declare share_target in the manifest

Per MDN, `share_target` requires an `action` (the URL that receives the share) and
`params` (the mapping of share fields to request parameters); `method` and `enctype`
are optional and default to `"GET"` and `"application/x-www-form-urlencoded"`:

```json
{
  "share_target": {
    "action": "/share-handler",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "title",
      "text": "text",
      "url": "url",
      "files": [
        { "name": "media", "accept": ["image/*", "video/*"] }
      ]
    }
  }
}
```

The default `GET` with no `files` is enough for text/URL-only sharing; receiving files
requires explicitly setting `"method": "POST"` and `"enctype": "multipart/form-data"`,
per MDN.

## 2. Handle the request in your service worker

Once the OS has offered your app in the share sheet and the user picks it, the browser
sends a `POST` (or `GET`) to `action` exactly like a form submission. Intercept it
in your service worker's `fetch` handler and read the shared data. MDN says a POST share
request is ideally answered with a `303` redirect to avoid repeated POST submissions on
refresh, and Chrome for Developers demonstrates that pattern:

```js
self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);
  if (event.request.method === 'POST' && url.pathname === '/share-handler') {
    event.respondWith((async () => {
      const formData = await event.request.formData();
      const files = formData.getAll('media');
      const cache = await caches.open('shared-content');
      await Promise.all(
        files.map((file, i) => cache.put(`/shared-file-${i}`, new Response(file)))
      );
      // Redirect (with the flag the app checks for a share launch) to a page
      // that reads the cached files back out.
      return Response.redirect('/share-handler/view?shared=1', 303);
    })());
  }
});
```

## 3. Read the shared data in the app

The redirected page reads whatever the service worker stored — here, the files cached
above — and shows them to the user before importing:

```js
async function loadSharedFiles() {
  const cache = await caches.open('shared-content');
  const keys = await cache.keys();
  const files = await Promise.all(
    keys
      .filter((req) => req.url.includes('/shared-file-'))
      .map((req) => cache.match(req).then((res) => res.blob()))
  );
  return files;
}
```

## Where it's supported

See the compatibility data for the exact browsers, platforms, and versions that support
`share_target`:

<CompatTable feature="manifest-share-target" />

## Detecting the redirected path and falling back

This example adds its own `shared` query parameter to the redirect. The page can detect
that application-defined redirect path and otherwise render its normal view:

```js
function isSharedRedirect() {
  return new URLSearchParams(location.search).has('shared');
}

async function loadSharedFiles() {
  if (!('caches' in window)) {
    // No Cache Storage support in this context — nothing to read back.
    return [];
  }
  const cache = await caches.open('shared-content');
  const keys = await cache.keys();
  return Promise.all(
    keys
      .filter((req) => req.url.includes('/shared-file-'))
      .map((req) => cache.match(req).then((res) => res.blob()))
  );
}

if (isSharedRedirect()) {
  loadSharedFiles().then(renderSharedFiles);
} else {
  // The app-defined redirect marker is absent — render the normal starting view.
  renderDefaultView();
}
```

## Practical checklist

- [ ] `share_target` only takes effect for an **installed** PWA — an unrelated tab is not
      offered in the OS share sheet.
- [ ] File sharing requires `"method": "POST"` and `"enctype": "multipart/form-data"`,
      per MDN — a GET-only target cannot receive `files`.
- [ ] Prefer a `303` redirect after handling the POST; MDN calls this ideal for avoiding
      repeated POST submissions when the page is refreshed.
- [ ] Give the redirected route an application-defined marker (e.g. a query parameter)
      so it can fall back to the normal UI when opened directly.
- [ ] Clear or expire cached shared files after reading them so repeated shares don't
      accumulate stale data.

## Where to go next

- [Manifest share_target reference](/reference/manifest/share-target/) — the full
  `share_target` syntax, including GET vs POST parameter mapping.
- [manifest: share_target support](/compatibility/manifest-share-target/) — per-browser
  compatibility data for this manifest member.